From b3a3e17aa2a60261534a855e0f0fe57cfd15a2e8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 3 Dec 2017 09:45:02 +0100 Subject: [PATCH 01/49] [RIX] Activated Mythicspoiler as RIX image download source --- .../mage/plugins/card/dl/sources/MythicspoilerComSource.java | 5 +++++ Mage.Client/src/main/resources/image.url.properties | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java index 03ba31aac40..2a468eabc8e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java @@ -253,6 +253,7 @@ public enum MythicspoilerComSource implements CardImageSource { supportedSets.add("C17"); supportedSets.add("IMA"); supportedSets.add("XLN"); + supportedSets.add("RIX"); sets = new LinkedHashMap<>(); setsAliases = new HashMap<>(); @@ -294,6 +295,10 @@ public enum MythicspoilerComSource implements CardImageSource { links.put("spitfirebastion", "spitfirebastion"); manualLinks.put("XLN", links); + HashMap linksRix = new HashMap<>(); + linksRix.put("vaultofcatlacan", "vaultofcatlacan"); + manualLinks.put("RIX", linksRix); + cardNameAliasesStart = new HashMap<>(); HashSet names = new HashSet<>(); names.add("eldrazidevastator.jpg"); diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index 0ded050d87f..4b3146cc6ee 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -74,6 +74,6 @@ dd3evg=ddaevg dd3gvl=ddagvl dd3jvc=ddajvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK,DDT,V17,RIX,E02,M19,M25,DOM,UST,H17 +ignore.urls=TOK,DDT,V17,E02,M19,M25,DOM,UST,H17 # sets ordered by release time (newest goes first) token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file From 26a45dd95151d315a26708ad385a8f54ffde6388 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 3 Dec 2017 17:09:02 +0100 Subject: [PATCH 02/49] * Root Sliver - Fixed that the Sliver spells can't be countered did not work correctly. --- Mage.Sets/src/mage/cards/r/RootSliver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/r/RootSliver.java b/Mage.Sets/src/mage/cards/r/RootSliver.java index facf6a51ffe..0190c80edf1 100644 --- a/Mage.Sets/src/mage/cards/r/RootSliver.java +++ b/Mage.Sets/src/mage/cards/r/RootSliver.java @@ -70,7 +70,7 @@ public class RootSliver extends CardImpl { // Root Sliver can't be countered. this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect())); // Sliver spells can't be countered by spells or abilities. - this.addAbility(new SimpleStaticAbility(Zone.STACK, new RootSliverEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RootSliverEffect())); } @@ -88,7 +88,7 @@ class RootSliverEffect extends ContinuousRuleModifyingEffectImpl { public RootSliverEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Sliver spells can't be countered by spells or abilities."; + staticText = "Sliver spells can't be countered by spells or abilities"; } public RootSliverEffect(final RootSliverEffect effect) { From 89fae4f33e13303203832d41ea516326c3500c6b Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 3 Dec 2017 20:33:32 +0400 Subject: [PATCH 03/49] Add svg symbols download from scryfall --- .../org/mage/card/arcane/ManaSymbols.java | 11 +- .../org/mage/plugins/card/CardPluginImpl.java | 12 +- .../org/mage/plugins/card/dl/DownloadJob.java | 1 - .../plugins/card/dl/sources/GathererSets.java | 2 +- .../dl/sources/ScryfallSymbolsSource.java | 171 ++++++++++++++++++ 5 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index fce453e0ef9..6ff5ffd881a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -291,11 +291,8 @@ public final class ManaSymbols { }; t.setTranscodingHints(transcoderHints); t.transcode(input, null); - } - catch (TranscoderException ex) { - // Requires Java 6 - ex.printStackTrace(); - throw new IOException("Couldn't convert " + svgFile); + } catch (Exception e) { + throw new IOException("Couldn't convert svg file: " + svgFile + " , reason: " + e.getMessage()); } finally { cssFile.delete(); @@ -348,7 +345,7 @@ public final class ManaSymbols { //imagePointer[0]; } - private static File getSymbolFileNameAsSVG(String symbol){ + public static File getSymbolFileNameAsSVG(String symbol){ return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg"); } @@ -364,7 +361,7 @@ public final class ManaSymbols { return loadSVG(sourceFile, resizeToWidth, resizeToHeight, true); } catch (Exception e) { - LOGGER.error("Can't load svg symbol: " + sourceFile.getPath()); + LOGGER.error("Can't load svg symbol: " + sourceFile.getPath() + " , reason: " + e.getMessage()); return null; } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 5825c34def2..8ef8279f8ac 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -21,6 +21,7 @@ import org.mage.plugins.card.dl.Downloader; import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.dl.sources.GathererSets; import org.mage.plugins.card.dl.sources.GathererSymbols; +import org.mage.plugins.card.dl.sources.ScryfallSymbolsSource; import org.mage.plugins.card.images.ImageCache; import org.mage.plugins.card.info.CardInfoPaneImpl; @@ -529,7 +530,9 @@ public class CardPluginImpl implements CardPlugin { public void downloadSymbols(String imagesDir) { final DownloadGui g = new DownloadGui(new Downloader()); - Iterable it = new GathererSymbols(); + Iterable it; + + it = new GathererSymbols(); for (DownloadJob job : it) { g.getDownloader().add(job); } @@ -539,6 +542,11 @@ public class CardPluginImpl implements CardPlugin { g.getDownloader().add(job); } + it = new ScryfallSymbolsSource(); + for (DownloadJob job : it) { + g.getDownloader().add(job); + } + /* it = new CardFrames(imagesDir); // TODO: delete frames download (not need now) for (DownloadJob job : it) { @@ -551,7 +559,7 @@ public class CardPluginImpl implements CardPlugin { g.getDownloader().add(job); } - JDialog d = new JDialog((Frame) null, "Download pictures", false); + JDialog d = new JDialog((Frame) null, "Download symbols", false); d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); d.addWindowListener(new WindowAdapter() { @Override diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java index 8d189b5796b..6efcfd3eef4 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java @@ -28,7 +28,6 @@ import org.mage.plugins.card.utils.CardImageUtils; public class DownloadJob extends AbstractLaternaBean { public enum State { - NEW, WORKING, FINISHED, ABORTED } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 8406c4e5415..57047e783df 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -313,4 +313,4 @@ public class GathererSets implements Iterable { String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity; return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst)); } -} +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java new file mode 100644 index 00000000000..caef7e5d211 --- /dev/null +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java @@ -0,0 +1,171 @@ +package org.mage.plugins.card.dl.sources; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.mage.plugins.card.dl.DownloadJob; + +import static org.mage.card.arcane.ManaSymbols.getSymbolFileNameAsSVG; +import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; + +// TODO: add force to download symbols (rewrite exist files) + +/** + * + * @author jaydi85@gmail.com + * + */ +public class ScryfallSymbolsSource implements Iterable { + + + static final String SOURCE_URL = "https://assets.scryfall.com/assets/scryfall.css"; // search css-file on https://scryfall.com/docs/api/colors + static final String STATE_PROP_NAME = "state"; + static final String DOWNLOAD_TEMP_FILE = getImagesDir() + File.separator + "temp" + File.separator + "scryfall-symbols-source.txt"; + + // card-symbol-(.{1,10}){background-image.+base64,(.+)("\)}) + // see https://regex101.com/ + static final String REGEXP_MANA_PATTERN = "card-symbol-(.{1,10})\\{background-image.+base64,(.+)(\"\\)\\})"; + + + protected static final org.apache.log4j.Logger LOGGER = org.apache.log4j.Logger.getLogger(ScryfallSymbolsSource.class); + + private static final int SYMBOLS_NUMBER_START = 0; + private static final int SYMBOLS_NUMBER_END = 20; + // copy-past symbols list from gatherer download + private static final String[] SYMBOLS_LIST = {"W", "U", "B", "R", "G", + "W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U", + "2/W", "2/U", "2/B", "2/R", "2/G", + "WP", "UP", "BP", "RP", "GP", + "X", "S", "T", "Q", "C", "E"}; + + @Override + public Iterator iterator() { + ArrayList jobs = new ArrayList<>(); + + // all symbols on one page + jobs.add(generateDownloadJob()); + + return jobs.iterator(); + } + + private void parseData(String sourcePath){ + + String sourceData = ""; + try { + sourceData = new String(Files.readAllBytes(Paths.get(sourcePath))); + }catch (IOException e) { + LOGGER.error("Can't open file to parse data: " + sourcePath + " , reason: " + e.getMessage()); + } + + // gen symbols list + ArrayList allMageSymbols = new ArrayList<>(); + for(int i = 0; i < SYMBOLS_LIST.length; i++){ + allMageSymbols.add(SYMBOLS_LIST[i]); + } + for(Integer i = SYMBOLS_NUMBER_START; i <= SYMBOLS_NUMBER_END; i++){ + allMageSymbols.add(String.valueOf(SYMBOLS_NUMBER_START + i)); + } + + Map foundedData = new HashMap<>(); + + // search raw data + sourceData = sourceData.replaceAll(".card-symbol", "\n.card-symbol"); // css as one line, but need multiline + Pattern regex = Pattern.compile(REGEXP_MANA_PATTERN); + Matcher regexMatcher = regex.matcher(sourceData); + while (regexMatcher.find()) { + String symbolCode = regexMatcher.group(1).trim(); + String symbolData = regexMatcher.group(2).trim().replace(" ", "").replaceAll("\n", ""); // decoder need only wrapped text as one line + + foundedData.put(symbolCode, symbolData); + } + + // dirs maker + File dir = getSymbolFileNameAsSVG("W").getParentFile(); + if(!dir.exists()){ + dir.mkdirs(); + } + + // decode and save data (only if not exist) + for(String needCode: allMageSymbols){ + + String searchCode = needCode.replace("/", ""); + + if(!foundedData.containsKey(searchCode)) + { + LOGGER.warn("Can't found symbol code from scryfall: " + searchCode); + continue; + } + + File destFile = getSymbolFileNameAsSVG(searchCode); + if (destFile.exists() && (destFile.length() > 0)){ + continue; + } + + try { + // base64 transform + String data64 = foundedData.get(searchCode); + Base64.Decoder dec = Base64.getDecoder(); + byte[] fileData = dec.decode(data64); + + FileOutputStream stream = new FileOutputStream(destFile); + stream.write(fileData); + stream.close(); + + LOGGER.info("New svg symbol downloaded: " + needCode); + } catch (Exception e) { + LOGGER.error("Can't decode svg icon and save to file: " + destFile.getPath() + ", reason: " + e.getMessage()); + } + } + } + + + private class ScryfallSymbolsDownloadJob extends DownloadJob{ + + // listener for data parse after download complete + private class ScryDownloadOnFinishedListener implements PropertyChangeListener { + private String downloadedFile; + + public ScryDownloadOnFinishedListener(String ADestFile){ + this.downloadedFile = ADestFile; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (!evt.getPropertyName().equals(STATE_PROP_NAME)){ + throw new IllegalArgumentException("Unknown download property " + evt.getPropertyName()); + } + + if (evt.getNewValue() != State.FINISHED){ + return; + } + + // parse data and save to dest + parseData(this.downloadedFile); + } + } + + private String destFile = ""; + + public ScryfallSymbolsDownloadJob() { + super("Scryfall symbols source", fromURL(SOURCE_URL), toFile(DOWNLOAD_TEMP_FILE)); + this.destFile = DOWNLOAD_TEMP_FILE; + this.addPropertyChangeListener(STATE_PROP_NAME, new ScryDownloadOnFinishedListener(this.destFile)); + + // clear dest file (always download new data) + File file = new File(this.destFile); + if (file.exists()){ + file.delete(); + } + } + } + + private DownloadJob generateDownloadJob() { + return new ScryfallSymbolsDownloadJob(); + } +} From ab8bcd92c23ec64ed8b6cbfa0722e0f4e682801d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 3 Dec 2017 17:39:00 +0100 Subject: [PATCH 04/49] * Some minor changes. --- .../src/mage/cards/a/ApocalypseDemon.java | 3 +- .../mage/test/cards/modal/OneOrBothTest.java | 214 +++++++++--------- 2 files changed, 108 insertions(+), 109 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java b/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java index 889d8d735c4..119c3cba427 100644 --- a/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java +++ b/Mage.Sets/src/mage/cards/a/ApocalypseDemon.java @@ -39,7 +39,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.target.common.TargetControlledPermanent; @@ -52,7 +51,6 @@ public class ApocalypseDemon extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another creature"); static { - filter.add(new CardTypePredicate(CardType.CREATURE)); filter.add(new AnotherPredicate()); } @@ -76,6 +74,7 @@ public class ApocalypseDemon extends CardImpl { super(card); } + @Override public ApocalypseDemon copy() { return new ApocalypseDemon(this); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java index 52aef300aaf..5ea1d4310cb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java @@ -1,107 +1,107 @@ -/* - * 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 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 OneOrBothTest extends CardTestPlayerBase { - - @Test - public void testSubtleStrikeFirstMode() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - // Choose one or both — - // • Target creature gets -1/-1 until end of turn. - // • Put a +1/+1 counter on target creature. - addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); - setModeChoice(playerA, "1"); - setModeChoice(playerA, null); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); - assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); - } - - @Test - public void testSubtleStrikeSecondMode() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - // Choose one or both — - // • Target creature gets -1/-1 until end of turn. - // • Put a +1/+1 counter on target creature. - addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); - setModeChoice(playerA, "2"); - setModeChoice(playerA, null); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); - assertPowerToughness(playerB, "Pillarfield Ox", 3, 5); - } - - @Test - public void testSubtleStrikeBothModes() { - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - // Choose one or both — - // • Target creature gets -1/-1 until end of turn. - // • Put a +1/+1 counter on target creature. - addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); - addTarget(playerA, "Silvercoat Lion"); - setModeChoice(playerA, "1"); - setModeChoice(playerA, "2"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); - assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); - } -} +/* + * 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 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 OneOrBothTest extends CardTestPlayerBase { + + @Test + public void testSubtleStrikeFirstMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, null); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); + } + + @Test + public void testSubtleStrikeSecondMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, null); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 3, 5); + } + + @Test + public void testSubtleStrikeBothModes() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + addTarget(playerA, "Silvercoat Lion"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + } +} From 8fac7a3dc16ef08a19a9abfa6c92bf363cf096b1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 3 Dec 2017 18:15:16 +0100 Subject: [PATCH 05/49] XMage 1.4.26V9 --- Mage.Common/src/main/java/mage/utils/MageVersion.java | 2 +- Mage/src/main/java/mage/cards/repository/CardRepository.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 4780e89977c..962b0e65972 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 26; - public final static String MAGE_VERSION_MINOR_PATCH = "V9b"; + public final static String MAGE_VERSION_MINOR_PATCH = "V9"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 59517a91ff3..7f50dec4965 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -58,7 +58,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 51; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 95; + private static final long CARD_CONTENT_VERSION = 96; private Dao cardDao; private Set classNames; From 35aa92ac72303148fc911c2152567ea64e09cc4c Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 6 Dec 2017 00:03:12 +1100 Subject: [PATCH 06/49] Fix face images. --- .../main/java/org/mage/plugins/card/utils/CardImageUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java index 919ad545d3a..e89c4158800 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -237,7 +237,7 @@ public final class CardImageUtils { } public static String generateFaceImagePath(String cardname, String set) { - return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + File.separator + ".jpg"; + return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + ".jpg"; } public static String generateTokenDescriptorImagePath(CardDownloadData card) { From 7f531caef138ff7ef4da6386ff5fc30d841a3c57 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 5 Dec 2017 20:25:37 +0100 Subject: [PATCH 07/49] Added missing trample --- Mage.Sets/src/mage/cards/g/GoblinRockSled.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/cards/g/GoblinRockSled.java b/Mage.Sets/src/mage/cards/g/GoblinRockSled.java index 8c2ad210f8b..3addc4904e9 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinRockSled.java +++ b/Mage.Sets/src/mage/cards/g/GoblinRockSled.java @@ -35,6 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; +import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -69,6 +70,9 @@ public class GoblinRockSled extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(1); + // Trample + this.addAbility(TrampleAbility.getInstance()); + // Goblin Rock Sled doesn't untap during your untap step if it attacked during your last turn. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapIfAttackedLastTurnSourceEffect()), new AttackedLastTurnWatcher()); From 1955dc6d18f726b36cf88c3b7aa9d55017522308 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Tue, 5 Dec 2017 20:30:51 +0100 Subject: [PATCH 08/49] Cleaned up imports --- Mage.Sets/src/mage/cards/d/DefensiveFormation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DefensiveFormation.java b/Mage.Sets/src/mage/cards/d/DefensiveFormation.java index babba4f36e6..c9b24d01302 100644 --- a/Mage.Sets/src/mage/cards/d/DefensiveFormation.java +++ b/Mage.Sets/src/mage/cards/d/DefensiveFormation.java @@ -32,7 +32,6 @@ import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; /** * From d6a08fbdd7f80c862e749b1107e47e5120856562 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Thu, 7 Dec 2017 01:01:52 +0100 Subject: [PATCH 09/49] NullPointerException fix --- Mage/src/main/java/mage/game/combat/Combat.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 45b7894b720..d26bc9ec608 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -578,6 +578,9 @@ public class Combat implements Serializable, Copyable { * @param game */ private void retrieveMustBlockAttackerRequirements(Player attackingPlayer, Game game) { + if (attackingPlayer == null) { + return; + } if (!game.getContinuousEffects().existRequirementEffects()) { return; } From 6802ebc537a951190ccda13edcdba2cd3303083c Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 7 Dec 2017 05:44:16 +0400 Subject: [PATCH 10/49] Fixed #4226 - wrong ability text in Drakestown Forgotten --- Mage.Sets/src/mage/cards/d/DrakestownForgotten.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/DrakestownForgotten.java b/Mage.Sets/src/mage/cards/d/DrakestownForgotten.java index a74dc5d4fdf..60ecd71c8ae 100644 --- a/Mage.Sets/src/mage/cards/d/DrakestownForgotten.java +++ b/Mage.Sets/src/mage/cards/d/DrakestownForgotten.java @@ -64,8 +64,8 @@ public class DrakestownForgotten extends CardImpl { new AddCountersSourceEffect( CounterType.P1P1.createInstance(), new CardsInAllGraveyardsCount(new FilterCreatureCard()), - false), - "with X +1/+1 counters on it, where X is the number of other creatures on the battlefield")); + false), + "with X +1/+1 counters on it, where X is the number of creature cards in all graveyards")); // {2}{B}, Remove a +1/+1 counter from Drakestown Forgotten: Target creature gets -1/-1 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-1, -1, Duration.EndOfTurn), new ManaCostsImpl<>("{2}{B}")); From 66a70dd116befa4914c08988f58df74161b1ce03 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 7 Dec 2017 06:05:04 +0400 Subject: [PATCH 11/49] - Fixed #4220 - card viewer raise error with alternative card numbers; - Fixed potential error on getCardsByRarity; - Added inner checks for wrong booster max card numbers settings; --- .../collection/viewer/MageBook.java | 2 +- .../plugins/card/dl/sources/GathererSets.java | 29 +++++++++++++++++-- .../main/java/mage/cards/ExpansionSet.java | 12 ++++++-- .../java/mage/cards/repository/CardInfo.java | 5 ++++ Mage/src/main/java/mage/util/CardUtil.java | 19 ++++++++++++ 5 files changed, 62 insertions(+), 5 deletions(-) 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 2dd577bd08b..623b5a434d5 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 @@ -464,7 +464,7 @@ public class MageBook extends JComponent { // first run for numbers list LinkedList haveNumbers = new LinkedList<>(); for (ExpansionSet.SetCardInfo card: cards){ - int cardNumber = Integer.parseInt(card.getCardNumber()); + int cardNumber = card.getCardNumberAsInt(); startNumber = min(startNumber, cardNumber); endNumber = Math.max(endNumber, cardNumber); haveNumbers.add(cardNumber); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 57047e783df..5330c542cf4 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -220,9 +220,10 @@ public class GathererSets implements Iterable { continue; // can't do other checks } - // 2. missing rarity icon: - // WARNING, need too much time (60+ secs), only for debug mode if (logger.isDebugEnabled()) { + // 2. missing rarity icon: + // WARNING, need too much time (60+ secs), only for debug mode + ///* if ((set.getCardsByRarity(Rarity.COMMON).size() > 0) && !res.haveCommon) { logger.error(String.format("Symbols: set have common cards, but don't download icon: %s (%s)", set.getCode(), set.getName())); } @@ -235,6 +236,30 @@ public class GathererSets implements Iterable { if ((set.getCardsByRarity(Rarity.MYTHIC).size() > 0) && !res.haveMyth) { logger.error(String.format("Symbols: set have mythic cards, but don't download icon: %s (%s)", set.getCode(), set.getName())); } + //*/ + + // 3. wrong sets config with alternative numbers + // TODO: some sets have cards above maxCardNumberInBooster, need to check it (search code for maxCardNumberInBooster), maybe delete at all after getCardNumberAsInt implement + if ((set.getMaxCardNumberInBooster() == 0) || (set.getMaxCardNumberInBooster() == Integer.MAX_VALUE)) + { + for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){ + if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()){ + logger.error(String.format("Symbols: set have alternative card but do not config to it: %s (%s)", set.getCode(), set.getName())); + break; + } + } + } + + // 4. have nonland card above maxboosternumber (info) + if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE) + { + for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){ + if (card.getRarity() == Rarity.LAND) { continue; } + if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()){ + logger.error(String.format("Symbols: set setup to cut off cards for boosters, non land card will be missing: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName())); + } + } + } } } diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 0b312c96aa1..790657e19bb 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -32,6 +32,7 @@ import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.CardUtil; import mage.util.RandomUtil; import java.io.Serializable; @@ -82,6 +83,10 @@ public abstract class ExpansionSet implements Serializable { return this.cardNumber; } + public int getCardNumberAsInt(){ + return CardUtil.parseCardNumberAsInt(this.cardNumber); + } + public Rarity getRarity() { return this.rarity; } @@ -388,9 +393,10 @@ public abstract class ExpansionSet implements Serializable { criteria.doubleFaced(false); } savedCardsInfos = CardRepository.instance.findCards(criteria); - // Workaround after card number is numeric + // Workaround after card number is numeric (p.s. card number is not numeric for some cards) + // TODO: some sets have фывфывфывфывфыв if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> Integer.valueOf(next.getCardNumber()) > maxCardNumberInBooster && rarity != Rarity.LAND); + savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster && rarity != Rarity.LAND); } savedCards.put(rarity, savedCardsInfos); @@ -431,4 +437,6 @@ public abstract class ExpansionSet implements Serializable { savedCards.clear(); } + public int getMaxCardNumberInBooster() { return maxCardNumberInBooster; } + } diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index 1600f5b82ad..7a725647d13 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -40,6 +40,7 @@ import mage.cards.*; import mage.cards.mock.MockCard; import mage.cards.mock.MockSplitCard; import mage.constants.*; +import mage.util.CardUtil; import mage.util.SubTypeList; import org.apache.log4j.Logger; @@ -374,6 +375,10 @@ public class CardInfo { return cardNumber; } + public int getCardNumberAsInt() { + return CardUtil.parseCardNumberAsInt(cardNumber); + } + public boolean isSplitCard() { return splitCard; } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index a91dd87bd07..301a26e83f2 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -382,6 +382,25 @@ public final class CardUtil { return true; } + + /** + * Parse card number as int (support base [123] and alternative numbers [123b]). + * + * @param cardNumber origin card number + * @return int + */ + public static int parseCardNumberAsInt(String cardNumber){ + + if (cardNumber.isEmpty()){ throw new IllegalArgumentException("Card number is empty.");} + + if(Character.isDigit(cardNumber.charAt(cardNumber.length() - 1))) + { + return Integer.parseInt(cardNumber); + }else{ + return Integer.parseInt(cardNumber.substring(0, cardNumber.length() - 1)); + } + } + /** * Creates and saves a (card + zoneChangeCounter) specific exileId. * From 5b7ab0de7a2ea6b3fb7ec8722608a8eaca5b121e Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 7 Dec 2017 08:41:59 +0400 Subject: [PATCH 12/49] typo --- .../plugins/card/dl/sources/GathererSets.java | 23 +++++++++---------- .../main/java/mage/cards/ExpansionSet.java | 3 +-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 5330c542cf4..1d01f9f7707 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -238,25 +238,24 @@ public class GathererSets implements Iterable { } //*/ - // 3. wrong sets config with alternative numbers - // TODO: some sets have cards above maxCardNumberInBooster, need to check it (search code for maxCardNumberInBooster), maybe delete at all after getCardNumberAsInt implement - if ((set.getMaxCardNumberInBooster() == 0) || (set.getMaxCardNumberInBooster() == Integer.MAX_VALUE)) - { - for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){ - if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()){ - logger.error(String.format("Symbols: set have alternative card but do not config to it: %s (%s)", set.getCode(), set.getName())); - break; - } + // 3. info: sets with alternative numbers + for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){ + if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()){ + logger.info(String.format("Symbols: set have alternative card but do not config to it: %s (%s)", set.getCode(), set.getName())); + break; } } - // 4. have nonland card above maxboosternumber (info) + // 4. info: sets with missing cards for boosters (todo: what about +20 number for alternative land arts?) if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE) { for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){ - if (card.getRarity() == Rarity.LAND) { continue; } if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()){ - logger.error(String.format("Symbols: set setup to cut off cards for boosters, non land card will be missing: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName())); + if (card.getRarity() == Rarity.LAND) { + logger.info(String.format("Symbols: set's booster have land above max card number: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName())); + }else { + logger.info(String.format("Symbols: set's booster missing nonland card:: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName())); + } } } } diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 790657e19bb..c14340386ed 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -393,8 +393,7 @@ public abstract class ExpansionSet implements Serializable { criteria.doubleFaced(false); } savedCardsInfos = CardRepository.instance.findCards(criteria); - // Workaround after card number is numeric (p.s. card number is not numeric for some cards) - // TODO: some sets have фывфывфывфывфыв + // Workaround after card number is numeric if (maxCardNumberInBooster != Integer.MAX_VALUE) { savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster && rarity != Rarity.LAND); } From 1efc062f6666d037b2429cdd49800d467e99d846 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 7 Dec 2017 22:46:31 +0400 Subject: [PATCH 13/49] Decks importer: + added support for new decklist text format from deckstats.net (see #4228); + added new checks to text format (too many empty lines, too big card number: more then 100); + fixed double window popup on cancel/close import from clipboard window; --- .../client/deckeditor/DeckEditorPanel.java | 10 +++- .../cards/decks/importer/TxtDeckImporter.java | 47 +++++++++++++++---- 2 files changed, 45 insertions(+), 12 deletions(-) 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 b7ae78ced5c..6612b5397c2 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -691,8 +691,10 @@ public class DeckEditorPanel extends javax.swing.JPanel { break; case 1: btnImportFromClipboardActionPerformed(evt); + break; case 2: btnImportFromClipboardActionWAppendPerformed(evt); + break; } }); @@ -814,9 +816,13 @@ public class DeckEditorPanel extends javax.swing.JPanel { dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { + Deck newDeck = null; try { - deck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true); - refreshDeck(); + newDeck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true); + if (newDeck != null) { + deck = newDeck; + refreshDeck(); + } } catch (GameException e1) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); } diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java index 089fbb1a6db..374cc3c94d3 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java @@ -47,27 +47,50 @@ public class TxtDeckImporter extends DeckImporter { public static final Set IGNORE_NAMES = new HashSet<>(Arrays.asList(SET_VALUES)); private boolean sideboard = false; + private boolean switchSideboardByEmptyLine = true; private int nonEmptyLinesTotal = 0; @Override protected void readLine(String line, DeckCardLists deckList) { - if (line.toLowerCase().contains("sideboard")) { - sideboard = true; - return; - } - if (line.startsWith("//")) { + + line = line.trim(); + + // switch sideboard by commands + String commentString = line.toLowerCase(); + if (commentString.startsWith("//")){ + // use start, not contains (card names may contain commands like "Legerdemain") + if (commentString.startsWith("//sideboard")) { + sideboard = true; + } else if (commentString.startsWith("//main")) { + sideboard = false; + + // if there are commands then disable empty line switcher (see deckstats.net format) + if (nonEmptyLinesTotal > 0){ + switchSideboardByEmptyLine = false; + } + } return; } - // Start the sideboard on empty line that follows - // at least 1 non-empty line - if (line.isEmpty() && nonEmptyLinesTotal > 0) { + // switch sideboard by empty line + if (switchSideboardByEmptyLine && line.isEmpty() && nonEmptyLinesTotal > 0) { + if(sideboard){ + sbMessage.append("Found empty line at ").append(lineCount).append(", but sideboard already used. Use //main, //sideboard switcher OR empty line to devide your cards.").append('\n'); + } sideboard = true; return; } else { nonEmptyLinesTotal++; } + // single line sideboard cards see https://deckstats.net/ + // SB: 3 Carnage Tyrant + boolean singleLineSideBoard = false; + if (line.startsWith("SB:")){ + line = line.replace("SB:", "").trim(); + singleLineSideBoard = true; + } + line = line.replace("\t", " "); // changing tabs to blanks as delimiter int delim = line.indexOf(' '); if (delim < 0) { @@ -87,12 +110,17 @@ public class TxtDeckImporter extends DeckImporter { } try { int num = Integer.parseInt(lineNum.replaceAll("\\D+", "")); + if ((num < 0) || (num > 100)){ + sbMessage.append("Invalid number (too small or too big): ").append(lineNum).append(" at line ").append(lineCount).append('\n'); + return; + } + CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true); if (cardInfo == null) { sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append('\n'); } else { for (int i = 0; i < num; i++) { - if (!sideboard) { + if (!sideboard && !singleLineSideBoard) { deckList.getCards().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode())); } else { deckList.getSideboard().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode())); @@ -103,5 +131,4 @@ public class TxtDeckImporter extends DeckImporter { sbMessage.append("Invalid number: ").append(lineNum).append(" at line ").append(lineCount).append('\n'); } } - } From 5fc0393bc7b794fc8f1e7655404bbf4d02de118b Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 8 Dec 2017 00:20:18 +0400 Subject: [PATCH 14/49] Deck editor: + added warning messages dialog after load/import decks with errors (instead throw exception); + added loading cursors on import-load decks; - fixed null exception error on empty/error deck loading; --- .../client/deckeditor/DeckEditorPanel.java | 100 ++++++++++++------ .../cards/decks/importer/DeckImporter.java | 22 +++- .../decks/importer/DeckImporterUtil.java | 8 +- 3 files changed, 96 insertions(+), 34 deletions(-) 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 6612b5397c2..3d0a2d5a660 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -805,6 +805,14 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(jSplitPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 615, Short.MAX_VALUE)); } + private void processAndShowImportErrors(StringBuilder errorMessages){ + // show up errors list + if (errorMessages.length() > 0){ + String mes = "Founded problems with deck: \n\n" + errorMessages.toString(); + JOptionPane.showMessageDialog(MageFrame.getDesktop(), mes.substring(0, Math.min(1000, mes.length())), "Errors while loading deck", JOptionPane.WARNING_MESSAGE); + } + } + /** * @param evt ActionEvent */ @@ -817,14 +825,22 @@ public class DeckEditorPanel extends javax.swing.JPanel { @Override public void windowClosed(WindowEvent e) { Deck newDeck = null; + StringBuilder errorMessages = new StringBuilder(); + + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { - newDeck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true); + newDeck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true); + processAndShowImportErrors(errorMessages); + if (newDeck != null) { deck = newDeck; refreshDeck(); } + } catch (GameException e1) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); + }finally { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } }); @@ -842,16 +858,22 @@ public class DeckEditorPanel extends javax.swing.JPanel { @Override public void windowClosed(WindowEvent e) { Deck deckToAppend = null; + StringBuilder errorMessages = new StringBuilder(); + + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { - deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true); + deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true); + processAndShowImportErrors(errorMessages); + if (deckToAppend != null) { deck = Deck.append(deckToAppend, deck); refreshDeck(); } } catch (GameException e1) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); + }finally { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } - } }); } @@ -881,20 +903,31 @@ public class DeckEditorPanel extends javax.swing.JPanel { } } } + + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { - setCursor(new Cursor(Cursor.WAIT_CURSOR)); - deck = Deck.load(DeckImporterUtil.importDeck(file.getPath()), true, true); + Deck newDeck = null; + StringBuilder errorMessages = new StringBuilder(); + + newDeck = Deck.load(DeckImporterUtil.importDeck(file.getPath(), errorMessages), true, true); + processAndShowImportErrors(errorMessages); + + if (newDeck != null) { + deck = newDeck; + refreshDeck(true); + } + + // save last deck history + try { + MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath()); + } catch (IOException ex) { + logger.error("Error on save last load deck folder: " + ex.getMessage()); + } + } catch (GameException ex) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - refreshDeck(true); - try { - if (file != null) { - MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath()); - } - } catch (IOException ex) { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } fcSelectDeck.setSelectedFile(null); @@ -930,7 +963,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { if (!fileName.endsWith(".dck")) { fileName += ".dck"; } - setCursor(new Cursor(Cursor.WAIT_CURSOR)); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); DeckCardLists cardLists = deck.getDeckCardLists(); cardLists.setCardLayout(deckArea.getCardLayout()); cardLists.setSideboardLayout(deckArea.getSideboardLayout()); @@ -938,7 +971,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE); } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } try { MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath()); @@ -973,29 +1006,36 @@ public class DeckEditorPanel extends javax.swing.JPanel { int ret = fcImportDeck.showOpenDialog(this); if (ret == JFileChooser.APPROVE_OPTION) { File file = fcImportDeck.getSelectedFile(); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { - setCursor(new Cursor(Cursor.WAIT_CURSOR)); DeckImporter importer = DeckImporterUtil.getDeckImporter(file.getPath()); + if (importer != null) { - deck = Deck.load(importer.importDeck(file.getPath())); - String errors = importer.getErrors(); - if (!errors.isEmpty()) { - JOptionPane.showMessageDialog(MageFrame.getDesktop(), errors, "Error importing deck", JOptionPane.ERROR_MESSAGE); + StringBuilder errorMessages = new StringBuilder(); + Deck newDeck = null; + + newDeck = Deck.load(importer.importDeck(file.getPath(), errorMessages)); + processAndShowImportErrors(errorMessages); + + if (newDeck != null) { + deck = newDeck; + refreshDeck(); } + + // save last deck import folder + try { + MageFrame.getPreferences().put("lastImportFolder", file.getCanonicalPath()); + } catch (IOException ex) { + logger.error("Error on save last used import folder: " + ex.getMessage()); + } + } else { JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Unknown deck format", "Error importing deck", JOptionPane.ERROR_MESSAGE); } } catch (Exception ex) { logger.fatal(ex); } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - refreshDeck(); - try { - if (file != null) { - MageFrame.getPreferences().put("lastImportFolder", file.getCanonicalPath()); - } - } catch (IOException ex) { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } fcImportDeck.setSelectedFile(null); @@ -1037,7 +1077,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private void btnGenDeckActionPerformed(ActionEvent evt) { try { - setCursor(new Cursor(Cursor.WAIT_CURSOR)); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); String path = DeckGenerator.generateDeck(); deck = Deck.load(DeckImporterUtil.importDeck(path), true, true); } catch (GameException ex) { @@ -1045,7 +1085,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { } catch (DeckGeneratorException ex) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Generator error", JOptionPane.ERROR_MESSAGE); } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } refreshDeck(); } 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 45600162174..b5d87b6c358 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -45,7 +45,14 @@ public abstract class DeckImporter { protected StringBuilder sbMessage = new StringBuilder(); //TODO we should stop using this not garbage collectable StringBuilder. It just bloats protected int lineCount; - public DeckCardLists importDeck(String file) { + + /** + * + * @param file file to import + * @param errorMessages you can setup output messages to showup to user (set null for fatal exception on messages.count > 0) + * @return decks list + */ + public DeckCardLists importDeck(String file, StringBuilder errorMessages) { File f = new File(file); DeckCardLists deckList = new DeckCardLists(); if (!f.exists()) { @@ -62,8 +69,15 @@ public abstract class DeckImporter { lineCount++; readLine(line, deckList); } + if (sbMessage.length() > 0) { - logger.fatal(sbMessage); + if(errorMessages != null) { + // normal output for user + errorMessages.append(sbMessage); + }else{ + // fatal error + logger.fatal(sbMessage); + } } } catch (Exception ex) { logger.fatal(null, ex); @@ -74,6 +88,10 @@ public abstract class DeckImporter { return deckList; } + public DeckCardLists importDeck(String file) { + return importDeck(file, null); + } + public String getErrors(){ return sbMessage.toString(); } diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java index da6d4c207e6..b846eb8c046 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java @@ -51,12 +51,16 @@ public final class DeckImporterUtil { } } - public static DeckCardLists importDeck(String file) { + public static DeckCardLists importDeck(String file, StringBuilder errorMessages) { DeckImporter deckImporter = getDeckImporter(file); if (deckImporter != null) { - return deckImporter.importDeck(file); + return deckImporter.importDeck(file, errorMessages); } else { return new DeckCardLists(); } } + + public static DeckCardLists importDeck(String file) { + return importDeck(file, null); + } } From 763594e579250a707ccd91f10fb79f712e626d61 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 8 Dec 2017 00:53:03 +0400 Subject: [PATCH 15/49] type --- .../java/mage/cards/decks/importer/TxtDeckImporter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java index 374cc3c94d3..f6be7960ca9 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java @@ -64,11 +64,13 @@ public class TxtDeckImporter extends DeckImporter { } else if (commentString.startsWith("//main")) { sideboard = false; - // if there are commands then disable empty line switcher (see deckstats.net format) - if (nonEmptyLinesTotal > 0){ + // if there are commands mode (see deckstats.net format) then disable empty line switcher + if (nonEmptyLinesTotal == 0){ switchSideboardByEmptyLine = false; } } + + // skip comment line return; } From b21ee39acfd2679da9db7bb7a286e25991b393ed Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 8 Dec 2017 12:44:43 +0100 Subject: [PATCH 16/49] Fix for Surveyor's Scope "X = 0" case didn't shuffle owner's library --- Mage.Sets/src/mage/cards/s/SurveyorsScope.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SurveyorsScope.java b/Mage.Sets/src/mage/cards/s/SurveyorsScope.java index 7621dce50ba..988c0fc6940 100644 --- a/Mage.Sets/src/mage/cards/s/SurveyorsScope.java +++ b/Mage.Sets/src/mage/cards/s/SurveyorsScope.java @@ -101,10 +101,8 @@ class SurveyorsScopeEffect extends OneShotEffect { } } game.informPlayers(new StringBuilder("Surveyor's Scope: X = ").append(numberOfLands).toString()); - if (numberOfLands > 0) { - return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, numberOfLands, StaticFilters.FILTER_BASIC_LAND_CARD)).apply(game, source); - } - return true; + // 10/17/2013 If no players control at least two more lands than you when the ability resolves, you’ll still search and shuffle your library. + return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, numberOfLands, StaticFilters.FILTER_BASIC_LAND_CARD)).apply(game, source); } return false; } From 6fa2f70ad178dff2b46ae2a284ef57af75917cb7 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 9 Dec 2017 18:30:20 +1100 Subject: [PATCH 17/49] Add in Unstable cards (possibly implementable ones at least) --- Utils/mtg-cards-data.txt | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 73f297ad8f9..7ca1ebc3375 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -32701,4 +32701,62 @@ Tetzimoc, Primal Death|Rivals of Ixalan|86|R|{4}{B}{B}|Legendary Creature - Elde Ghalta, Primal Hunger|Rivals of Ixalan|130|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.$Trample| Storm the Vault|Rivals of Ixalan|173|R|{2}{U}{R}|Legendary Enchantment|||Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."$At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.| Vault of Catlacan|Rivals of Ixalan|173|R||Legendary Land|||(Transforms from Storm the Vault.)${T}: Add one mana of any color to your mana pool.${T}: Add {U} to your mana pool for each artifact you control.| -The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.| \ No newline at end of file +The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.| +Angelic Rocket|Unstable|139|R|{8}|Host Creature - Angel|4|4|Flying$When this creature enters the battlefield, you may destroy target nonland permanent.| +As Luck Would Have It|Unstable|102|R|{G}|Enchantment|||Hexproof$Whenever you roll a die, put a number of luck counters on As Luck Would Have It equal to the result. Then is there are 100 or more luck counters on As Luck Would Have It, you win the game. (Count both rolls if you reroll a die.)| +Baron Von Count|Unstable|127|M|{1}{B}{R}|Legendary Creature - Human Villain|3|3|Baron Von Count enters the battlefield with a doom counter on "5."$Whenever you cast a spell with the indicated numeral in its mana cost, text box, power, or toughness, move the doom counter one numeral to the left.$When the doom counter moves from "1," destroy target player and put that doom counter on "5."| +Big Boa Constrictor|Unstable|51|C|{3}{B}|Host Creature - Snake|1|2|When this creature enters the battlefield, roll a six-sided die. Target opponent loses life equal to the result.| +Bumbling Pangolin|Unstable|78|C|{3}{R}|Host Creature - Beast|2|2|When this creature enters the battlefield, you may destroy target artifact.| +Buzzing Whack-a-Doodle|Unstable|141|U|{4}|Artifact|||As Buzzing Whack-a-Doodle enters the battlefield, you and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, Buzzing Whack-a-Doodle has that ability. Otherwise it has Buzz.$*Whack - T: Target player loses 2 life. $*Doodle - T: You gain 3 life.$*Buzz - 2, T: Draw a card.| +Chittering Doom|Unstable|104|U|{3}{G}|Enchantment|||Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token.| +Common Iguana|Unstable|79|C|{1}{R}|Host Creature - Lizard|1|3|When this creature enters the battlefield, you may discard a card. If you do, draw a card.| +Contraption Cannon|Unstable|144|U|{4}|Artifact|||2, Sacrifice Contraption Cannon: It deals damage to target creature or player equal to the number of Contraptions you control.| +Crow Storm|Unstable|31|U|{2}{U}|Sorcery|||Create a 1/2 blue Bird creature token with flying named Storm Crow.$Storm (When you cast this spell, copy it for each spell cast before it this turn.)| +Curious Killbot|Unstable|145|C|{2}|Artifact Creature - Killbot|2|1|| +Dirty Rat|Unstable|53|C|{1}{B}|Host Creature - Rat|1|1|When this creature enters the battlefield, target opponent discards a card.| +Eager Beaver|Unstable|107|C|{2}{G}|Host Creature - Beaver|3|2|When this creature enters the battlefield, you may untap target permanent.| +Earl of Squirrel|Unstable|108|R|{4}{G}{G}|Creature - Squirrel Advisor|4|4|Squirrellink (Damage dealt by this creature also causes you to create that many 1/1 green Squirrel creature tokens.)$Creature tokens you control are Squirrels in addition to their other creature types.$Other Squirrels you control get +1/+1.| +Feisty Stegosaurus|Unstable|81|C|{4}{R}|Host Creature - Dinosaur|2|1|When this creature enters the battlefield, roll a six-sided die. This creature deals damage equal to the result to target creature an opponent controls.| +Forest|Unstable|216|C||Basic Land - Forest|||G| +GO TO JAIL|Unstable|8|C|{W}|Enchantment|||When GO TO JAIL enters the battlefield, exile target creature an opponent controls until GO TO JAIL leaves the battlefield.$At the beginning of the upkeep of the exiled card's owner, that player rolls two six-sided dice. If he or she rolls doubles, sacrifice GO TO JAIL.| +Gnome-Made Engine|Unstable|148|C|{4}|Host Creature - Construct|2|2|When this creature enters the battlefield, create a 1/1 colorless Gnome artifact creature token.| +Ground Pounder|Unstable|110|C|{1}{G}|Creature - Goblin Warrior|2|2|3G: Roll a six-sided die. Ground Pounder gets +X/+X until end of turn, where X is the result.$Whenever you roll a 5 or higher on a die, Ground Pounder gains trample until end of turn.| +Hammer Helper|Unstable|85|C|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result.| +Hammer Jammer|Unstable|86|U|{3}{R}|Creature - Goblin Warrior|0|0|As Hammer Jammer enters the battlefield, roll a six-sided die. Hammer Jammer enters the battlefield with a number of +1/+1 counters on it equal to the result.$Whenever you roll a die, remove all +1/+1 counters from Hammer Jammer, then put a number of +1/+1 counters on it equal to the result.| +Hangman|Unstable|56|R|{B}|Creature - Human Villain|1|1|As Hangman enters the battlefield, secretly note a word with six to eight letters.$1: Target player who doesn't control Hangman guesses he noted word or an unguessed letter in that word. If he or she guesses wrong, put a +1/+1 counter on Hangman. Any player may activate this ability.$When a player guesses the noted word of all of its letters, sacrifice Hangman.| +Hazmat Suit (Used)|Unstable|57|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+1 and has menace.$Whenever a player's skin or fingernail touches enchanted creature, that player loses 2 life.| +Hot Fix|Unstable|133|R|{4}{W}{U}|Sorcery||| You have ten seconds to look at and rearrange the cards in your library. At the end of those ten seconds, if you're touching one or more of those cards, shuffle your library.| +Hydradoodle|Unstable|112|R|{X}{X}{G}{G}|Creature - Hydra Hound|0|0|As Hydradoodle enters the battlefield, roll X six-sided dice. Hydradoodle enters the battlefield with s number of +1/+1 counters on it equal to the total of those results.$Reach, trample| +Incite Insight|Unstable|36|R|{X}{U}{U}|Sorcery|||Assemble X Contraptions.| +Inhumaniac|Unstable|59|U|{1}{B}|Creature - Brainiac|1|1|At the beginning of your upkeep, roll a six-sided die. On a 3 or 4, put a +1/+1 counter on Inhumaniac. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from Inhumaniac.| +Island|Unstable|213|C||Basic Land - Island|||U| +Jackknight|Unstable|11|R|{1}{W}|Artifact Creature - Cyborg Knight|1|1|Whenever another artifact enters the battlefield under your control, put a +1/+1 counter on Jackknight. If that artifact is a Contraption, Jackknight gains lifelink until end of turn.| +Krark's Other Thumb|Unstable|151|U|{2}|Legendary Artifact|||If you would roll a die, instead roll two of those dice and ignore one of those results.| +Lobe Lobber|Unstable|153|U|{2}|Artifact - Equipment|||Equipped creature has "T: This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it."$Equip 2| +Mad Science Fair Project|Unstable|154|C|{3}|Artifact|||T: Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool.| +Mer Man|Unstable|39|C|{4}{U}|Host Creature - Human Fish|3|3|When this creature enters the battlefield, you may draw a card.| +Mountain|Unstable|215|C||Basic Land - Mountain|||R| +Numbing Jellyfish|Unstable|42|C|{3}{U}|Host Creature - Jellyfish|2|3|When this creature enters the battlefield, roll a six-sided die. Target player puts the top X cards of his or her library into his or her graveyard, where X is the result.| +Ordinary Pony|Unstable|17|C|{2}{W}|Host Creature - Horse|2|3|When this creature enters the battlefield, you may exile target non-Horse creature you control, then return it to the battlefield under its owner's control.| +Painiac|Unstable|91|C|{2}{R}|Creature - Brainiac|0|3|At the beginning of your upkeep, roll a six-sided die. Painiac gets +X/+0 until end of turn, where X is the result.| +Plains|Unstable|212|C||Basic Land - Plains|||W| +Shaggy Camel|Unstable|22|C|{3}{W}|Host Creature - Camel|3|3|When this creature enters the battlefield, creatures you control get +1/+1 until end of turn.| +Skull Saucer|Unstable|66|U|{4}{B}{B}|Creature - Zombie Head|4|1|Flying$When Skull Saucer enters the battlefield, destroy target creature and put your head on the table. Sacrifice Skull Saucer when your head stops touching the table.| +Snickering Squirrel|Unstable|68|C|{B}|Creature - Squirrel Advisor|1|1|You may tap Snickering Squirrel to increase the result of a die any player rolled by 1.| +Split Screen|Unstable|158|R|{4}|Artifact|||When Split Screen enters the battlefield, shuffle your library and deal it into four libraries. If anything refers to your library, choose one of your libraries for it.$Play with your libraries' top cards revealed.$When Split Screen leaves the battlefield, shuffle your libraries together.| +Spy Eye|Unstable|46|U|{2}{U}{U}|Creature - Eye Spy|1|3|Flying$Whenever Spy Eye deals combat damage to a player, you may draw a card from that player's library.| +Squirrel-Powered Scheme|Unstable|70|U|{2}{B}|Enchantment|||Increase the result of each die you roll by 2.| +Staff of the Letter Magus|Unstable|159|U|{3}|Artifact|||As Staff of the Letter Magus enters the battlefield, choose a consonant other than N, R, S, or T.$Whenever a player casts a spell, you gain 1 life for each time the chosen letter appears in that spell's name.| +Steamflogger Boss|Unstable|93|R|{3}{R}|Creature - Goblin Rigger|3|3|Other Riggers you control get +1/+0 and have haste.$If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead.| +Steamflogger Service Rep|Unstable|124|U|{2}{G}|Creature - Goblin Rigger|1|1|Whenever another Goblin enters the battlefield under your control, you may pay 1. If you do, Steamflogger Service Rep assembles a Contraption.| +Steel Squirrel|Unstable|162|U|{2}|Artifact Creature - Squirrel|1|1|Whenever you roll a 5 or higher on a die, Steel Squirrel gets +X/+X until end of turn, where X is the result.$6: Roll a six-sided die.| +Stinging Scorpion|Unstable|72|C|{4}{B}|Host Creature - Scorpion|3|2|When this creature enters the battlefield, target creature an opponent controls gets -1/-1 until end of turn.| +Summon the Pack|Unstable|74|M|{7}{B}|Sorcery|||Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types. (Remove those cards from your deck before beginning a new game)| +Swamp|Unstable|214|C||Basic Land - Swamp|||B| +Sword of Dungeons and Dragons|Unstable|1|M|{3}|Artifact - Equipment|||Equipped creature gets +2/+2 and has protection from Rogues and from Clerics.$Whenever equipped creature deals combat damage to a player, create a 4/4 gold Dragon creature token with flying and roll a d20. If you roll a 20, repeat this process.$Equip {2}| +The Big Idea|Unstable|76|R|{4}{R}{R}|Legendary Creature - Brainiac Villain|4|4|2{BR}{BR}, T: Roll a six-sided dice. Create a number of 1/1 red Brainiac creature tokens equal to the result. $Tap three untapped Brainiacs you control: The next time you would roll a six-sided die, instead roll two six-sided dice and use the total of those results.| +Time Out|Unstable|48|C|{4}{U}|Instant|||Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result.| +Voracious Vacuum|Unstable|164|C|{3}|Host Creature - Construct|1|1|When this creature enters the battlefield, put a +1/+1 counter on target creature.| +Wall of Fortune|Unstable|50|C|{1}{U}|Artifact Creature - Wall|0|4|Defender$You may tap an untapped Wall you control to have any player reroll a die that player rolled.| +Wild Crocodile|Unstable|125|C|{1}{G}|Host Creature - Crocodile|1|1|When this creature enters the battlefield, search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| +Willing Test Subject|Unstable|126|C|{2}{G}|Creature- Spider Monkey Scientist|2|2|Reach$Whenever you roll a 4 or higher on a die, put a +1/+1 counter on Willing Test Subject.$6: Roll a six-sided die.| From dce69c18dc86eb147ec78d4bf3c98733179b516e Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 9 Dec 2017 18:52:24 +0400 Subject: [PATCH 18/49] + improved deck import from deckstats --- .../decks/importer/DeckImporterUtil.java | 31 +++++++++++- .../cards/decks/importer/TxtDeckImporter.java | 48 +++++++++++-------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java index b846eb8c046..b19a5850c07 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java @@ -29,19 +29,48 @@ package mage.cards.decks.importer; import mage.cards.decks.DeckCardLists; +import java.io.File; +import java.util.Scanner; + /** * * @author North */ public final class DeckImporterUtil { + public static final String[] SIDEBOARD_MARKS = new String[]{"//sideboard", "sb: "}; + + public static boolean haveSideboardSection(String file){ + // search for sideboard section: + // or //sideboard + // or SB: 1 card name -- special deckstats.net + + File f = new File(file); + try (Scanner scanner = new Scanner(f)) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim().toLowerCase(); + + for(String mark: SIDEBOARD_MARKS){ + if (line.startsWith(mark)){ + return true; + } + } + } + } catch (Exception e) { + // ignore error, deckimporter will process it + } + + // not found + return false; + } + public static DeckImporter getDeckImporter(String file) { if (file.toLowerCase().endsWith("dec")) { return new DecDeckImporter(); } else if (file.toLowerCase().endsWith("mwdeck")) { return new MWSDeckImporter(); } else if (file.toLowerCase().endsWith("txt")) { - return new TxtDeckImporter(); + return new TxtDeckImporter(haveSideboardSection(file)); } else if (file.toLowerCase().endsWith("dck")) { return new DckDeckImporter(); } else if (file.toLowerCase().endsWith("dek")) { diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java index f6be7960ca9..de7904451ee 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java @@ -47,45 +47,55 @@ public class TxtDeckImporter extends DeckImporter { public static final Set IGNORE_NAMES = new HashSet<>(Arrays.asList(SET_VALUES)); private boolean sideboard = false; - private boolean switchSideboardByEmptyLine = true; + private boolean switchSideboardByEmptyLine = true; // all cards after first empty line will be sideboard (like mtgo format) private int nonEmptyLinesTotal = 0; + public TxtDeckImporter(boolean haveSideboardSection){ + if(haveSideboardSection){ + switchSideboardByEmptyLine = false; + } + } + @Override protected void readLine(String line, DeckCardLists deckList) { line = line.trim(); - // switch sideboard by commands + // process comment: + // skip or force to sideboard String commentString = line.toLowerCase(); if (commentString.startsWith("//")){ // use start, not contains (card names may contain commands like "Legerdemain") + if (commentString.startsWith("//sideboard")) { sideboard = true; - } else if (commentString.startsWith("//main")) { - sideboard = false; - - // if there are commands mode (see deckstats.net format) then disable empty line switcher - if (nonEmptyLinesTotal == 0){ - switchSideboardByEmptyLine = false; - } } // skip comment line return; } - // switch sideboard by empty line - if (switchSideboardByEmptyLine && line.isEmpty() && nonEmptyLinesTotal > 0) { - if(sideboard){ - sbMessage.append("Found empty line at ").append(lineCount).append(", but sideboard already used. Use //main, //sideboard switcher OR empty line to devide your cards.").append('\n'); - } - sideboard = true; - return; - } else { - nonEmptyLinesTotal++; + // remove inner card comments from text line: 2 Blinding Fog #some text (like deckstats format) + int commentDelim = line.indexOf('#'); + if(commentDelim >= 0){ + line = line.substring(0, commentDelim).trim(); } - // single line sideboard cards see https://deckstats.net/ + // switch sideboard by empty line + if (switchSideboardByEmptyLine && line.isEmpty() && nonEmptyLinesTotal > 0) { + if(!sideboard){ + sideboard = true; + }else{ + sbMessage.append("Found empty line at ").append(lineCount).append(", but sideboard already used. Use //sideboard switcher OR one empty line to devide your cards.").append('\n'); + } + + // skip empty line + return; + } + + nonEmptyLinesTotal++; + + // single line sideboard card from deckstats.net // SB: 3 Carnage Tyrant boolean singleLineSideBoard = false; if (line.startsWith("SB:")){ From 6b7b8ba00ae137ffe249aa23f867a68baf5dd989 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 9 Dec 2017 11:19:04 -0500 Subject: [PATCH 19/49] Implemented Target Minotaur --- .../src/mage/cards/t/TargetMinotaur.java | 65 +++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 1 + Utils/mtg-cards-data.txt | 1 + 3 files changed, 67 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TargetMinotaur.java diff --git a/Mage.Sets/src/mage/cards/t/TargetMinotaur.java b/Mage.Sets/src/mage/cards/t/TargetMinotaur.java new file mode 100644 index 00000000000..e8d4156be94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TargetMinotaur.java @@ -0,0 +1,65 @@ +/* + * 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.t; + +import java.util.UUID; +import mage.MageInt; +import mage.constants.SubType; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author TheElk801 + */ +public class TargetMinotaur extends CardImpl { + + public TargetMinotaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Prowess + this.addAbility(new ProwessAbility()); + + } + + public TargetMinotaur(final TargetMinotaur card) { + super(card); + } + + @Override + public TargetMinotaur copy() { + return new TargetMinotaur(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 338e9e4d380..211e08d8c5c 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -46,5 +46,6 @@ public class Unstable extends ExpansionSet { private Unstable() { super("Unstable", "UST", ExpansionSet.buildDate(2017, 12, 8), SetType.JOKESET); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); + cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); } } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 7ca1ebc3375..5c7fa05f7b1 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -32754,6 +32754,7 @@ Stinging Scorpion|Unstable|72|C|{4}{B}|Host Creature - Scorpion|3|2|When this cr Summon the Pack|Unstable|74|M|{7}{B}|Sorcery|||Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types. (Remove those cards from your deck before beginning a new game)| Swamp|Unstable|214|C||Basic Land - Swamp|||B| Sword of Dungeons and Dragons|Unstable|1|M|{3}|Artifact - Equipment|||Equipped creature gets +2/+2 and has protection from Rogues and from Clerics.$Whenever equipped creature deals combat damage to a player, create a 4/4 gold Dragon creature token with flying and roll a d20. If you roll a 20, repeat this process.$Equip {2}| +Target Minotaur|Unstable|98|C|{1}{R}|Creature - Minotaur Warrior|2|1|Prowess| The Big Idea|Unstable|76|R|{4}{R}{R}|Legendary Creature - Brainiac Villain|4|4|2{BR}{BR}, T: Roll a six-sided dice. Create a number of 1/1 red Brainiac creature tokens equal to the result. $Tap three untapped Brainiacs you control: The next time you would roll a six-sided die, instead roll two six-sided dice and use the total of those results.| Time Out|Unstable|48|C|{4}{U}|Instant|||Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result.| Voracious Vacuum|Unstable|164|C|{3}|Host Creature - Construct|1|1|When this creature enters the battlefield, put a +1/+1 counter on target creature.| From 5b5c6bd7a7447b5e164bc91af0bec3876810ffad Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 9 Dec 2017 11:29:15 -0500 Subject: [PATCH 20/49] Implemented Amateur Auteur --- Mage.Sets/src/mage/cards/a/AmateurAuteur.java | 70 +++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 1 + Utils/mtg-cards-data.txt | 1 + 3 files changed, 72 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AmateurAuteur.java diff --git a/Mage.Sets/src/mage/cards/a/AmateurAuteur.java b/Mage.Sets/src/mage/cards/a/AmateurAuteur.java new file mode 100644 index 00000000000..12968ab7497 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmateurAuteur.java @@ -0,0 +1,70 @@ +/* + * 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.a; + +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.effects.common.DestroyTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.target.common.TargetEnchantmentPermanent; + +/** + * + * @author TheElk801 + */ +public class AmateurAuteur extends CardImpl { + + public AmateurAuteur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Sacrifice Amateur Auteur: Destroy target enchantment. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); + ability.addTarget(new TargetEnchantmentPermanent()); + this.addAbility(ability); + } + + public AmateurAuteur(final AmateurAuteur card) { + super(card); + } + + @Override + public AmateurAuteur copy() { + return new AmateurAuteur(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 211e08d8c5c..8eab374cce4 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -45,6 +45,7 @@ public class Unstable extends ExpansionSet { private Unstable() { super("Unstable", "UST", ExpansionSet.buildDate(2017, 12, 8), SetType.JOKESET); + cards.add(new SetCardInfo("Amateur Auteur", 3, Rarity.COMMON, mage.cards.a.AmateurAuteur.class)); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 5c7fa05f7b1..c9452bc9fb1 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -32702,6 +32702,7 @@ Ghalta, Primal Hunger|Rivals of Ixalan|130|R|{10}{G}{G}|Legendary Creature - Eld Storm the Vault|Rivals of Ixalan|173|R|{2}{U}{R}|Legendary Enchantment|||Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."$At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.| Vault of Catlacan|Rivals of Ixalan|173|R||Legendary Land|||(Transforms from Storm the Vault.)${T}: Add one mana of any color to your mana pool.${T}: Add {U} to your mana pool for each artifact you control.| The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.| +Amateur Auteur|Unstable|3|C|{1}{W}|Creature - Human|2|2|Sacrifice Amateur Auteur: Destroy target enchantment.| Angelic Rocket|Unstable|139|R|{8}|Host Creature - Angel|4|4|Flying$When this creature enters the battlefield, you may destroy target nonland permanent.| As Luck Would Have It|Unstable|102|R|{G}|Enchantment|||Hexproof$Whenever you roll a die, put a number of luck counters on As Luck Would Have It equal to the result. Then is there are 100 or more luck counters on As Luck Would Have It, you win the game. (Count both rolls if you reroll a die.)| Baron Von Count|Unstable|127|M|{1}{B}{R}|Legendary Creature - Human Villain|3|3|Baron Von Count enters the battlefield with a doom counter on "5."$Whenever you cast a spell with the indicated numeral in its mana cost, text box, power, or toughness, move the doom counter one numeral to the left.$When the doom counter moves from "1," destroy target player and put that doom counter on "5."| From f09a54128221d4d2ddfae105ea1de4757fac7d82 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 9 Dec 2017 21:26:22 +0400 Subject: [PATCH 21/49] - fixed test --- .../java/org/mage/test/decks/importer/TxtDeckImporterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/decks/importer/TxtDeckImporterTest.java b/Mage.Tests/src/test/java/org/mage/test/decks/importer/TxtDeckImporterTest.java index aa351e47f89..c4b1dbd4685 100644 --- a/Mage.Tests/src/test/java/org/mage/test/decks/importer/TxtDeckImporterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/decks/importer/TxtDeckImporterTest.java @@ -12,7 +12,7 @@ public class TxtDeckImporterTest { @Test public void testImportWithBlankLineAboveSideboard() { - TxtDeckImporter importer = new TxtDeckImporter(); + TxtDeckImporter importer = new TxtDeckImporter(false); CardInfo card; DeckCardLists deck = new DeckCardLists(); From 8e3daf54a9a0becf18c1c7063cbef3a02fcdc132 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 10 Dec 2017 22:49:55 +1100 Subject: [PATCH 22/49] Start adding in Dice Roll effects --- .../src/mage/cards/a/AsLuckWouldHaveIt.java | 150 ++++++++++++++++++ Mage.Sets/src/mage/cards/c/CrowStorm.java | 62 ++++++++ Mage.Sets/src/mage/cards/g/GroundPounder.java | 148 +++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 11 ++ .../java/org/mage/test/player/TestPlayer.java | 11 ++ .../java/org/mage/test/stub/PlayerStub.java | 11 ++ .../main/java/mage/game/events/GameEvent.java | 1 + Mage/src/main/java/mage/players/Player.java | 4 + .../main/java/mage/players/PlayerImpl.java | 27 +++- 9 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/a/AsLuckWouldHaveIt.java create mode 100644 Mage.Sets/src/mage/cards/c/CrowStorm.java create mode 100644 Mage.Sets/src/mage/cards/g/GroundPounder.java diff --git a/Mage.Sets/src/mage/cards/a/AsLuckWouldHaveIt.java b/Mage.Sets/src/mage/cards/a/AsLuckWouldHaveIt.java new file mode 100644 index 00000000000..1a5c89a8c76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AsLuckWouldHaveIt.java @@ -0,0 +1,150 @@ +/* + * 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.a; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.Counter; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class AsLuckWouldHaveIt extends CardImpl { + + static final String rule = "put a number of luck counters on {this} equal to the result. Then if there are 100 or more luck counters on {this}, you win the game."; + + public AsLuckWouldHaveIt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // Whenever you roll a die, put a number of luck counters on As Luck Would Have It equal to the result. Then if there are 100 or more luck counters on As Luck Would Have It, you win the game. + this.addAbility(new AsLuckWouldHaveItTriggeredAbility()); + } + + public AsLuckWouldHaveIt(final AsLuckWouldHaveIt card) { + super(card); + } + + @Override + public AsLuckWouldHaveIt copy() { + return new AsLuckWouldHaveIt(this); + } +} + +class AsLuckWouldHaveItTriggeredAbility extends TriggeredAbilityImpl { + + public AsLuckWouldHaveItTriggeredAbility() { + super(Zone.BATTLEFIELD, new AsLuckWouldHaveItEffect(), false); + } + + public AsLuckWouldHaveItTriggeredAbility(final AsLuckWouldHaveItTriggeredAbility ability) { + super(ability); + } + + @Override + public AsLuckWouldHaveItTriggeredAbility copy() { + return new AsLuckWouldHaveItTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DICE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) { + for (Effect effect : this.getEffects()) { + effect.setValue("rolled", event.getAmount()); + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever you roll a die, " + super.getRule(); + } +} + +class AsLuckWouldHaveItEffect extends OneShotEffect { + + public AsLuckWouldHaveItEffect() { + super(Outcome.Benefit); + this.staticText = "put a number of luck counters on {this} equal to the result. Then if there are 100 or more luck counters on {this}, you win the game."; + } + + public AsLuckWouldHaveItEffect(final AsLuckWouldHaveItEffect effect) { + super(effect); + } + + @Override + public AsLuckWouldHaveItEffect copy() { + return new AsLuckWouldHaveItEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + if (getValue("rolled") != null) { + int amount = (Integer) getValue("rolled"); + permanent.addCounters(new Counter("luck", amount), source, game); + + if (permanent.getCounters(game).getCount("luck") >= 100) { + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.won(game); + } + } + + return true; + } + } + return false; + + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrowStorm.java b/Mage.Sets/src/mage/cards/c/CrowStorm.java new file mode 100644 index 00000000000..8ba10dcf889 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrowStorm.java @@ -0,0 +1,62 @@ +/* + * 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.c; + +import java.util.UUID; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.StormAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.StormCrowToken; + +/** + * + * @author spjspj + */ +public class CrowStorm extends CardImpl { + + public CrowStorm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Create a 1/2 blue Bird creature token with flying named Storm Crow. + this.getSpellAbility().addEffect(new CreateTokenEffect(new StormCrowToken(), 1)); + + // Storm (When you cast this spell, copy it for each spell cast before it this turn.) + this.addAbility(new StormAbility()); + } + + public CrowStorm(final CrowStorm card) { + super(card); + } + + @Override + public CrowStorm copy() { + return new CrowStorm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GroundPounder.java b/Mage.Sets/src/mage/cards/g/GroundPounder.java new file mode 100644 index 00000000000..e1993427415 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GroundPounder.java @@ -0,0 +1,148 @@ +/* + * 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.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +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.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class GroundPounder extends CardImpl { + + public GroundPounder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // 3G: Roll a six-sided die. Ground Pounder gets +X/+X until end of turn, where X is the result. + //this.addAbility(new SimpleActivatedAbility(new RollDiceEffect(new CreateTokenEffect(new WireflyToken()), 6), new ManaCostsImpl("{3}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GroundPounderEffect(), new ManaCostsImpl("{3}{G}"))); + + // Whenever you roll a 5 or higher on a die, Ground Pounder gains trample until end of turn. + this.addAbility(new GroundPounderTriggeredAbility()); + } + + public GroundPounder(final GroundPounder card) { + super(card); + } + + @Override + public GroundPounder copy() { + return new GroundPounder(this); + } +} + +class GroundPounderEffect extends OneShotEffect { + + public GroundPounderEffect() { + super(Outcome.Benefit); + this.staticText = "Roll a six-sided die. {this} gets +X/+X until end of turn, where X is the result"; + } + + public GroundPounderEffect(final GroundPounderEffect effect) { + super(effect); + } + + @Override + public GroundPounderEffect copy() { + return new GroundPounderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + int amount = controller.rollDice(game, 6); + game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source); + return true; + } + return false; + } +} + +class GroundPounderTriggeredAbility extends TriggeredAbilityImpl { + + public GroundPounderTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), false); + } + + public GroundPounderTriggeredAbility(final GroundPounderTriggeredAbility ability) { + super(ability); + } + + @Override + public GroundPounderTriggeredAbility copy() { + return new GroundPounderTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DICE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) { + if (event.getAmount() >= 5) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you roll a 5 or higher on a die, {this} gains trample until end of turn"; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 338e9e4d380..465a7fa9913 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -27,7 +27,9 @@ */ package mage.sets; +import mage.cards.CardGraphicInfo; import mage.cards.ExpansionSet; +import mage.cards.FrameStyle; import mage.constants.Rarity; import mage.constants.SetType; @@ -45,6 +47,15 @@ public class Unstable extends ExpansionSet { private Unstable() { super("Unstable", "UST", ExpansionSet.buildDate(2017, 12, 8), SetType.JOKESET); + cards.add(new SetCardInfo("As Luck Would Have It", 102, Rarity.RARE, mage.cards.a.AsLuckWouldHaveIt.class)); + cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); + cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); + cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); + } } 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 595a54d6e60..68b35334ca3 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 @@ -1825,6 +1825,17 @@ public class TestPlayer implements Player { return computerPlayer.flipCoin(game, appliedEffects); } + @Override + public int rollDice(Game game, int numSides) { + return computerPlayer.rollDice(game, numSides); + } + + @Override + public int rollDice(Game game, ArrayList appliedEffects, int numSides) { + return computerPlayer.rollDice(game, appliedEffects, numSides); + } + + @Override public List getAvailableAttackers(Game game) { return computerPlayer.getAvailableAttackers(game); 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 e082f0a0b90..fd469eb8fc7 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 @@ -646,6 +646,17 @@ public class PlayerStub implements Player { return false; } + @Override + public int rollDice(Game game, int numSides) { + return 1; + } + + @Override + public int rollDice(Game game, ArrayList appliedEffects, int numSides) { + return 1; + } + + @Override public void discard(int amount, Ability source, Game game) { diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index b6b65c48476..34c8357a0dc 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -229,6 +229,7 @@ public class GameEvent implements Serializable { ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL, + ROLL_DICE, DICE_ROLLED, PAID_CUMULATIVE_UPKEEP, DIDNT_PAY_CUMULATIVE_UPKEEP, //permanent events diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 71478eb2f98..eb72ad1fac4 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -417,6 +417,10 @@ public interface Player extends MageItem, Copyable { boolean flipCoin(Game game, ArrayList appliedEffects); + int rollDice(Game game, int numSides); + + int rollDice(Game game, ArrayList appliedEffects, int numSides); + @Deprecated void discard(int amount, Ability source, Game game); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 7168fcbcd69..57775dc82ab 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2312,7 +2312,7 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean flipCoin(Game game) { return this.flipCoin(game, null); } - + /** * @param game * @param appliedEffects @@ -2331,6 +2331,31 @@ public abstract class PlayerImpl implements Player, Serializable { } return event.getFlag(); } + + @Override + public int rollDice(Game game, int numSides) { + return this.rollDice(game, null, numSides); + } + + /** + * @param game + * @param appliedEffects + * @return the number that the player rolled + */ + @Override + public int rollDice(Game game, ArrayList appliedEffects, int numSides) { + int result = RandomUtil.nextInt(numSides); + if (!game.isSimulation()) { + game.informPlayers("[Roll a dice] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice"); + } + GameEvent event = new GameEvent(GameEvent.EventType.ROLL_DICE, playerId, null, playerId, result, true); + event.setAppliedEffects(appliedEffects); + event.setAmount(result); + if (!game.replaceEvent(event)) { + game.fireEvent(new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, result, event.getFlag())); + } + return result; + } @Override public List getAvailableAttackers(Game game) { From eca218881cafe69269f2dc6e88d95a219c3486da Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 10 Dec 2017 22:51:08 +1100 Subject: [PATCH 23/49] Start adding in Dice Roll effects --- .../effects/common/RollDiceEffect.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java new file mode 100644 index 00000000000..7af75c31292 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java @@ -0,0 +1,107 @@ +/* + * 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.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 spjspj + */ +public class RollDiceEffect extends OneShotEffect { + + protected Effects executingEffects = new Effects(); + protected int numSides; + + public RollDiceEffect(Effect effect, int numSides) { + this(effect, Outcome.Neutral, numSides); + } + + public RollDiceEffect(Effect effect, Outcome outcome, int numSides) { + super(outcome); + addEffect(effect); + this.numSides = numSides; + } + + public RollDiceEffect(final RollDiceEffect effect) { + super(effect); + this.executingEffects = effect.executingEffects.copy(); + this.numSides = effect.numSides; + } + + public void addEffect(Effect effect) { + if (effect != null) { + executingEffects.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 : executingEffects) { + int rolled = controller.rollDice(game, numSides); + effect.setTargetPointer(this.targetPointer); + effect.setValue("rolled", rolled); + this.setValue("rolled", rolled); + 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("Roll a " + numSides + " sided dice and then ").append(executingEffects.getText(mode)); + return sb.toString(); + } + + @Override + public RollDiceEffect copy() { + return new RollDiceEffect(this); + } +} From eb9f50e37036bc81d2a9307295e9fac2ae059523 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 10 Dec 2017 23:01:55 +1100 Subject: [PATCH 24/49] Crow Storm! --- .../game/permanent/token/StormCrowToken.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Mage/src/main/java/mage/game/permanent/token/StormCrowToken.java diff --git a/Mage/src/main/java/mage/game/permanent/token/StormCrowToken.java b/Mage/src/main/java/mage/game/permanent/token/StormCrowToken.java new file mode 100644 index 00000000000..a06ffdb32eb --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/StormCrowToken.java @@ -0,0 +1,50 @@ +/* +* 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.game.permanent.token; + +import mage.constants.CardType; +import mage.constants.SubType; +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; + +/** + * + * @author spjspj + */ +public class StormCrowToken extends Token { + + public StormCrowToken() { + super("Storm Crow", "1/2 blue Bird creature token with flying named Storm Crow"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add(SubType.BIRD); + power = new MageInt(1); + toughness = new MageInt(2); + this.addAbility(FlyingAbility.getInstance()); + } +} From 89a45eaa4d9da2d9224de7a1de8c6048d0846ea7 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 10 Dec 2017 23:22:10 +1100 Subject: [PATCH 25/49] Fix type. --- Mage.Sets/src/mage/cards/g/GroundPounder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/g/GroundPounder.java b/Mage.Sets/src/mage/cards/g/GroundPounder.java index e1993427415..27d709a74ad 100644 --- a/Mage.Sets/src/mage/cards/g/GroundPounder.java +++ b/Mage.Sets/src/mage/cards/g/GroundPounder.java @@ -59,7 +59,7 @@ public class GroundPounder extends CardImpl { public GroundPounder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); - this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(2); From f39e4fd4eec6a30f3ffae50b72229b40a0c68b71 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 10 Dec 2017 17:43:37 +0400 Subject: [PATCH 26/49] - added card download for unstable set from scryfall; - changed client version from 9 to 10b; --- .../org/mage/plugins/card/dl/sources/ScryfallImageSource.java | 4 ++-- .../java/org/mage/plugins/card/images/DownloadPictures.java | 3 +++ Mage.Client/src/main/resources/image.url.properties | 3 +-- Mage.Common/src/main/java/mage/utils/MageVersion.java | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) 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 b95cc3fefd3..111e4480a3f 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 @@ -204,8 +204,8 @@ public enum ScryfallImageSource implements CardImageSource { supportedSets.add("IMA"); // supportedSets.add("E02"); // supportedSets.add("V17"); -// supportedSets.add("UST"); -// supportedSets.add("RIX"); + supportedSets.add("UST"); + supportedSets.add("RIX"); // supportedSets.add("A25"); // supportedSets.add("DOM"); // supportedSets.add("M19"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java index 59a6be5bc5a..02ba9bb2a01 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java @@ -43,6 +43,8 @@ import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable { + // don't forget to remove new sets from ignore.urls to download (propeties file in resources) + private static DownloadPictures instance; private static final Logger logger = Logger.getLogger(DownloadPictures.class); @@ -772,6 +774,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab // START to download cardImageSource.doPause(url.getPath()); URLConnection httpConn = url.openConnection(p); + httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2"); httpConn.connect(); int responseCode = ((HttpURLConnection) httpConn).getResponseCode(); diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index 4b3146cc6ee..620a028344e 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -29,7 +29,6 @@ ulg=ul 6ed=6e btd=bd sth=sh -nem=ne por=po s99=st lgn=le @@ -74,6 +73,6 @@ dd3evg=ddaevg dd3gvl=ddagvl dd3jvc=ddajvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK,DDT,V17,E02,M19,M25,DOM,UST,H17 +ignore.urls=TOK,DDT,V17,E02,M19,M25,DOM,H17 # sets ordered by release time (newest goes first) token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 962b0e65972..2ef3e3430b0 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 26; - public final static String MAGE_VERSION_MINOR_PATCH = "V9"; + public final static String MAGE_VERSION_MINOR_PATCH = "V10b"; public final static String MAGE_VERSION_INFO = ""; private final int major; From 1eaf1857554d43b56ba922542cc026c3c153355d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 11 Dec 2017 04:01:55 +0400 Subject: [PATCH 27/49] Fixed #4236 (Forked Bold in DDP have wrong rarity) --- Mage.Sets/src/mage/sets/ZendikarVsEldrazi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/ZendikarVsEldrazi.java b/Mage.Sets/src/mage/sets/ZendikarVsEldrazi.java index a39362d81f0..c50645e3dd7 100644 --- a/Mage.Sets/src/mage/sets/ZendikarVsEldrazi.java +++ b/Mage.Sets/src/mage/sets/ZendikarVsEldrazi.java @@ -69,7 +69,7 @@ public class ZendikarVsEldrazi extends ExpansionSet { cards.add(new SetCardInfo("Forest", 38, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 39, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 40, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forked Bolt", 60, Rarity.UNCOMMON, mage.cards.f.ForkedBolt.class)); + cards.add(new SetCardInfo("Forked Bolt", 60, Rarity.COMMON, mage.cards.f.ForkedBolt.class)); cards.add(new SetCardInfo("Frontier Guide", 12, Rarity.UNCOMMON, mage.cards.f.FrontierGuide.class)); cards.add(new SetCardInfo("Graypelt Hunter", 13, Rarity.COMMON, mage.cards.g.GraypeltHunter.class)); cards.add(new SetCardInfo("Graypelt Refuge", 32, Rarity.UNCOMMON, mage.cards.g.GraypeltRefuge.class)); From 7c855990c9a0111a427ae0d3a870cd147a29a19c Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 12 Dec 2017 00:08:37 +1100 Subject: [PATCH 28/49] Add 1 unstable card. Fix dice roll (was only returning '0-(n-1)') --- .../mage/cards/b/BoxOfFreerangeGoblins.java | 92 +++++++++++++++++++ Mage.Sets/src/mage/cards/g/GroundPounder.java | 1 - Mage.Sets/src/mage/sets/Unstable.java | 1 + .../main/java/mage/players/PlayerImpl.java | 2 +- Utils/mtg-cards-data.txt | 1 + 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/b/BoxOfFreerangeGoblins.java diff --git a/Mage.Sets/src/mage/cards/b/BoxOfFreerangeGoblins.java b/Mage.Sets/src/mage/cards/b/BoxOfFreerangeGoblins.java new file mode 100644 index 00000000000..16771460577 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoxOfFreerangeGoblins.java @@ -0,0 +1,92 @@ +/* + * 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.b; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.token.GoblinToken; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class BoxOfFreerangeGoblins extends CardImpl { + + public BoxOfFreerangeGoblins(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); + + // Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result. + this.getSpellAbility().addEffect(new BoxOfFreerangeGoblinsEffect()); + } + + public BoxOfFreerangeGoblins(final BoxOfFreerangeGoblins card) { + super(card); + } + + @Override + public BoxOfFreerangeGoblins copy() { + return new BoxOfFreerangeGoblins(this); + } +} + +class BoxOfFreerangeGoblinsEffect extends OneShotEffect { + + public BoxOfFreerangeGoblinsEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result"; + } + + public BoxOfFreerangeGoblinsEffect(final BoxOfFreerangeGoblinsEffect effect) { + super(effect); + } + + @Override + public BoxOfFreerangeGoblinsEffect copy() { + return new BoxOfFreerangeGoblinsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int amount = controller.rollDice(game, 6); + CreateTokenEffect effect = new CreateTokenEffect(new GoblinToken(), amount); + effect.apply(game, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GroundPounder.java b/Mage.Sets/src/mage/cards/g/GroundPounder.java index 27d709a74ad..640ffcbee0f 100644 --- a/Mage.Sets/src/mage/cards/g/GroundPounder.java +++ b/Mage.Sets/src/mage/cards/g/GroundPounder.java @@ -65,7 +65,6 @@ public class GroundPounder extends CardImpl { this.toughness = new MageInt(2); // 3G: Roll a six-sided die. Ground Pounder gets +X/+X until end of turn, where X is the result. - //this.addAbility(new SimpleActivatedAbility(new RollDiceEffect(new CreateTokenEffect(new WireflyToken()), 6), new ManaCostsImpl("{3}{G}"))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GroundPounderEffect(), new ManaCostsImpl("{3}{G}"))); // Whenever you roll a 5 or higher on a die, Ground Pounder gains trample until end of turn. diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 525da39b9f6..1bbb71d79f7 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -50,6 +50,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Amateur Auteur", 3, Rarity.COMMON, mage.cards.a.AmateurAuteur.class)); cards.add(new SetCardInfo("As Luck Would Have It", 102, Rarity.RARE, mage.cards.a.AsLuckWouldHaveIt.class)); + cards.add(new SetCardInfo("Box of Free-Range Goblins", 77, Rarity.COMMON, mage.cards.b.BoxOfFreerangeGoblins.class)); cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 57775dc82ab..f16b7938ee3 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2344,7 +2344,7 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public int rollDice(Game game, ArrayList appliedEffects, int numSides) { - int result = RandomUtil.nextInt(numSides); + int result = RandomUtil.nextInt(numSides) + 1; if (!game.isSimulation()) { game.informPlayers("[Roll a dice] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice"); } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index c9452bc9fb1..51622f8aa3d 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -32747,6 +32747,7 @@ Snickering Squirrel|Unstable|68|C|{B}|Creature - Squirrel Advisor|1|1|You may ta Split Screen|Unstable|158|R|{4}|Artifact|||When Split Screen enters the battlefield, shuffle your library and deal it into four libraries. If anything refers to your library, choose one of your libraries for it.$Play with your libraries' top cards revealed.$When Split Screen leaves the battlefield, shuffle your libraries together.| Spy Eye|Unstable|46|U|{2}{U}{U}|Creature - Eye Spy|1|3|Flying$Whenever Spy Eye deals combat damage to a player, you may draw a card from that player's library.| Squirrel-Powered Scheme|Unstable|70|U|{2}{B}|Enchantment|||Increase the result of each die you roll by 2.| +Box of Freerange Goblins|Unstable|77|C|{4}{R}{R}|Sorcery |||Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result.| Staff of the Letter Magus|Unstable|159|U|{3}|Artifact|||As Staff of the Letter Magus enters the battlefield, choose a consonant other than N, R, S, or T.$Whenever a player casts a spell, you gain 1 life for each time the chosen letter appears in that spell's name.| Steamflogger Boss|Unstable|93|R|{3}{R}|Creature - Goblin Rigger|3|3|Other Riggers you control get +1/+0 and have haste.$If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead.| Steamflogger Service Rep|Unstable|124|U|{2}{G}|Creature - Goblin Rigger|1|1|Whenever another Goblin enters the battlefield under your control, you may pay 1. If you do, Steamflogger Service Rep assembles a Contraption.| From fe02e3fba3bdf727517471a6e7d97ad7691c779d Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 12 Dec 2017 01:02:51 +1100 Subject: [PATCH 29/49] Fix points for Aus 7pt Highlander --- .../src/mage/deck/AusHighlander.java | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) 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 aad84f38392..caae0aed3fc 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 @@ -114,7 +114,39 @@ public class AusHighlander extends Constructed { int totalPoints = 0; for (Map.Entry entry : counts.entrySet()) { String cn = entry.getKey(); - + if (cn.equals("Ancestral Recall") + || cn.equals("Black Lotus") + || cn.equals("Time Vault")) { + totalPoints += 4; + invalid.put(cn, "4 points"); + } + if (cn.equals("Demonic Tutor") + || cn.equals("Imperial Seal") + || cn.equals("Mox Emerald") + || cn.equals("Mox Jet") + || cn.equals("Mox Pearl") + || cn.equals("Mox Ruby") + || cn.equals("Sol Ring") + || cn.equals("Time Walk") + || cn.equals("Tinker") + || cn.equals("Vampiric Tutor") + || cn.equals("Yawgmoth's Will") + || cn.equals("Mox Sapphire.")) { + totalPoints += 3; + invalid.put(cn, "3 points"); + } + if (cn.equals("Channel") + || cn.equals("Dig Through Time") + || cn.equals("Library of Alexandria") + || cn.equals("Mana Crypt") + || cn.equals("Mystical Tutor") + || cn.equals("Protean Hulk") + || cn.equals("Skullclamp") + || cn.equals("Strip Mine") + || cn.equals("Tolarian Academy.")) { + totalPoints += 2; + invalid.put(cn, "2 points"); + } if (cn.equals("Back to Basics") || cn.equals("Balance") || cn.equals("Birthing Pod") @@ -139,8 +171,8 @@ public class AusHighlander extends Constructed { || cn.equals("Natural Order") || cn.equals("Oath of Druids") || cn.equals("Personal Tutor") + || cn.equals("Sensei's Divining Top") || cn.equals("Snapcaster Mage") - || cn.equals("Steelshaper's Gift") || cn.equals("Stoneforge Mystic") || cn.equals("Survival of the Fittest") || cn.equals("Tainted Pact") @@ -151,50 +183,15 @@ public class AusHighlander extends Constructed { || cn.equals("Umezawa's Jitte") || cn.equals("Wasteland") || cn.equals("Wheel of Fortune") - || cn.equals("Worldly Tutor") - || cn.equals("Yawgmoth's Bargain")) { + || cn.equals("Yawgmoth's Bargain") + || cn.equals("Worldly Tutor")) { totalPoints += 1; invalid.put(cn, "1 point"); } - - if (cn.equals("Channel") - || cn.equals("Dig Through Time") - || cn.equals("Library of Alexandria") - || cn.equals("Mana Crypt") - || cn.equals("Mox Emerald") - || cn.equals("Mox Jet") - || cn.equals("Mox Pearl") - || cn.equals("Mox Ruby") - || cn.equals("Mox Sapphire") - || cn.equals("Mystical Tutor") - || cn.equals("Protean Hulk") - || cn.equals("Skullclamp") - || cn.equals("Strip Mine") - || cn.equals("Tolarian Academy")) { - totalPoints += 2; - invalid.put(cn, "2 points"); - } - - if (cn.equals("Demonic Tutor") - || cn.equals("Imperial Seal") - || cn.equals("Sol Ring") - || cn.equals("Time Walk") - || cn.equals("Tinker") - || cn.equals("Vampiric Tutor") - || cn.equals("Yawgmoth's Will")) { - totalPoints += 3; - invalid.put(cn, "3 points"); - } - - if (cn.equals("Ancestral Recall") - || cn.equals("Black Lotus") - || cn.equals("Time Vault")) { - totalPoints += 4; - invalid.put(cn, "4 points"); - } } 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!"); valid = false; } return valid; From 6c02186f2c47761645ed6663ba6bc7a5a1a49241 Mon Sep 17 00:00:00 2001 From: spjspj Date: Wed, 13 Dec 2017 09:07:01 +1100 Subject: [PATCH 30/49] Add 2 unstable cards. --- Mage.Sets/src/mage/cards/h/HammerHelper.java | 103 +++++++++++++++++++ Mage.Sets/src/mage/cards/p/Painiac.java | 100 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + 3 files changed, 205 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HammerHelper.java create mode 100644 Mage.Sets/src/mage/cards/p/Painiac.java diff --git a/Mage.Sets/src/mage/cards/h/HammerHelper.java b/Mage.Sets/src/mage/cards/h/HammerHelper.java new file mode 100644 index 00000000000..bf54276c572 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HammerHelper.java @@ -0,0 +1,103 @@ +/* + * 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.h; + +import java.util.UUID; +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.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +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.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author spjspj + */ +public class HammerHelper extends CardImpl { + + public HammerHelper(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 and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new HammerHelperEffect()); + } + + public HammerHelper(final HammerHelper card) { + super(card); + } + + @Override + public HammerHelper copy() { + return new HammerHelper(this); + } +} + +class HammerHelperEffect extends OneShotEffect { + + HammerHelperEffect() { + super(Outcome.Benefit); + staticText = "Gain control of target creature until end of turn. Untap that creature and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result"; + } + + HammerHelperEffect(HammerHelperEffect effect) { + super(effect); + } + + @Override + public HammerHelperEffect copy() { + return new HammerHelperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent targetCreature = game.getPermanent(source.getFirstTarget()); + if (controller != null && targetCreature != null) { + source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); + game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn), source); + targetCreature.untap(game); + int amount = controller.rollDice(game, 6); + game.addEffect(new BoostTargetEffect(amount, 0, Duration.EndOfTurn), source); + game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/p/Painiac.java b/Mage.Sets/src/mage/cards/p/Painiac.java new file mode 100644 index 00000000000..71beaa79219 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/Painiac.java @@ -0,0 +1,100 @@ +/* + * 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.p; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +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.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class Painiac extends CardImpl { + + public Painiac(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add("Brainiac"); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // At the beginning of your upkeep, roll a six-sided die. Painiac gets +X/+0 until end of turn, where X is the result. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PainiacEffect(), TargetController.YOU, false)); + } + + public Painiac(final Painiac card) { + super(card); + } + + @Override + public Painiac copy() { + return new Painiac(this); + } +} + +class PainiacEffect extends OneShotEffect { + + public PainiacEffect() { + super(Outcome.Benefit); + this.staticText = "Roll a six-sided die. {this} gets +X/+0 until end of turn, where X is the result"; + } + + public PainiacEffect(final PainiacEffect effect) { + super(effect); + } + + @Override + public PainiacEffect copy() { + return new PainiacEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + int amount = controller.rollDice(game, 6); + game.addEffect(new BoostSourceEffect(amount, 0, Duration.EndOfTurn), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 1bbb71d79f7..a8c71f83a6f 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -54,8 +54,10 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); + cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); From 7645f10202bd933bb29e5213e933156e14204d02 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 14 Dec 2017 10:14:48 +1100 Subject: [PATCH 31/49] Add 2 unstable cards. --- Mage.Sets/src/mage/cards/p/Painiac.java | 3 ++- Mage/src/main/java/mage/constants/SubType.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/p/Painiac.java b/Mage.Sets/src/mage/cards/p/Painiac.java index 71beaa79219..44d005f6784 100644 --- a/Mage.Sets/src/mage/cards/p/Painiac.java +++ b/Mage.Sets/src/mage/cards/p/Painiac.java @@ -38,6 +38,7 @@ 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.game.Game; import mage.game.permanent.Permanent; @@ -51,8 +52,8 @@ public class Painiac extends CardImpl { public Painiac(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.subtype.add(SubType.BRAINIAC); - this.subtype.add("Brainiac"); this.power = new MageInt(0); this.toughness = new MageInt(3); diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 755d1ea658f..2f1ac8b644e 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -72,6 +72,7 @@ public enum SubType { BITH("Bith", SubTypeSet.CreatureType, true), // Star Wars BLINKMOTH("Blinkmoth", SubTypeSet.CreatureType), BOAR("Boar", SubTypeSet.CreatureType), + BRAINIAC("Brainiac", SubTypeSet.CreatureType), BRINGER("Bringer", SubTypeSet.CreatureType), BRUSHWAGG("Brushwagg", SubTypeSet.CreatureType), // C From 6c643fdbd728195dde820ffafab2e499142f7b25 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 14 Dec 2017 16:44:42 +1100 Subject: [PATCH 32/49] Add 2 unstable cards. --- .../mage/cards/m/MadScienceFairProject.java | 132 ++++++++++++++++++ .../src/mage/cards/w/WillingTestSubject.java | 120 ++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + 3 files changed, 254 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MadScienceFairProject.java create mode 100644 Mage.Sets/src/mage/cards/w/WillingTestSubject.java diff --git a/Mage.Sets/src/mage/cards/m/MadScienceFairProject.java b/Mage.Sets/src/mage/cards/m/MadScienceFairProject.java new file mode 100644 index 00000000000..3b503600c35 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MadScienceFairProject.java @@ -0,0 +1,132 @@ +/* + * 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.m; + +import java.util.List; +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +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.ChoiceColor; +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; + +/** + * + * @author spjspj + */ +public class MadScienceFairProject extends CardImpl { + + public MadScienceFairProject(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {tap}: Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool. + this.addAbility(new MadScienceFairProjectManaAbility()); + } + + public MadScienceFairProject(final MadScienceFairProject card) { + super(card); + } + + @Override + public MadScienceFairProject copy() { + return new MadScienceFairProject(this); + } +} + +class MadScienceFairProjectManaAbility extends ActivatedManaAbilityImpl { + + public MadScienceFairProjectManaAbility() { + super(Zone.BATTLEFIELD, new MadScienceFairManaEffect(), new TapSourceCost()); + } + + public MadScienceFairProjectManaAbility(final MadScienceFairProjectManaAbility ability) { + super(ability); + } + + @Override + public MadScienceFairProjectManaAbility copy() { + return new MadScienceFairProjectManaAbility(this); + } +} + +class MadScienceFairManaEffect extends ManaEffect { + + public MadScienceFairManaEffect() { + super(); + this.staticText = "Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool"; + } + + public MadScienceFairManaEffect(final MadScienceFairManaEffect effect) { + super(effect); + } + + @Override + public MadScienceFairManaEffect copy() { + return new MadScienceFairManaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + int amount = controller.rollDice(game, 6); + if (amount <= 3) { + controller.getManaPool().addMana(Mana.ColorlessMana(1), game, source); + } else { + ChoiceColor choice = new ChoiceColor(); + controller.choose(Outcome.PutManaInPool, choice, game); + if (choice.getColor() == null) { + return false; + } + Mana chosen = choice.getMana(1); + if (chosen != null) { + checkToFirePossibleEvents(chosen, game, source); + controller.getManaPool().addMana(chosen, game, source); + return true; + } + } + return true; + } + return false; + } + + @Override + public Mana getMana(Game game, Ability source) { + return null; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WillingTestSubject.java b/Mage.Sets/src/mage/cards/w/WillingTestSubject.java new file mode 100644 index 00000000000..ebafcff0a1a --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WillingTestSubject.java @@ -0,0 +1,120 @@ +/* + * 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.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.RollDiceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +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.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author spjspj + */ +public class WillingTestSubject extends CardImpl { + + public WillingTestSubject(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.MONKEY); + this.subtype.add(SubType.SCIENTIST); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Whenever you roll a 4 or higher on a die, put a +1/+1 counter on Willing Test Subject. + this.addAbility(new WillingTestSubjectTriggeredAbility()); + + // 6: Roll a six-sided die. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RollDiceEffect(null, 6), new GenericManaCost(6)); + this.addAbility(ability); + } + + public WillingTestSubject(final WillingTestSubject card) { + super(card); + } + + @Override + public WillingTestSubject copy() { + return new WillingTestSubject(this); + } +} + +class WillingTestSubjectTriggeredAbility extends TriggeredAbilityImpl { + + public WillingTestSubjectTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + + } + + public WillingTestSubjectTriggeredAbility(final WillingTestSubjectTriggeredAbility ability) { + super(ability); + } + + @Override + public WillingTestSubjectTriggeredAbility copy() { + return new WillingTestSubjectTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DICE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) { + if (event.getAmount() >= 4) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you roll a 4 or higher on a die, put a +1/+1 counter on {this}"; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index a8c71f83a6f..c64225ccd3a 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -56,11 +56,13 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); + cards.add(new SetCardInfo("Willing Test Subject", 126, Rarity.COMMON, mage.cards.w.WillingTestSubject.class)); } } From f39a0d4a777ad440a5546b5ee2ec4037ad70f862 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 14 Dec 2017 19:21:53 +1100 Subject: [PATCH 33/49] Add 2 unstable cards. --- .../src/mage/cards/s/SnickeringSquirrel.java | 120 ++++++++++++++++++ .../mage/cards/s/SquirrelPoweredScheme.java | 102 +++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + .../effects/common/RollDiceEffect.java | 17 +-- .../src/main/java/mage/constants/SubType.java | 3 +- 5 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java create mode 100644 Mage.Sets/src/mage/cards/s/SquirrelPoweredScheme.java diff --git a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java new file mode 100644 index 00000000000..e0ff21a6e86 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java @@ -0,0 +1,120 @@ +/* + * 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.s; + +import java.util.UUID; +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.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.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class SnickeringSquirrel extends CardImpl { + + public SnickeringSquirrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add("Squirrel"); + this.subtype.add("Advisor"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // You may tap Snickering Squirrel to increase the result of a die any player rolled by 1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SnickeringSquirrelEffect())); + } + + public SnickeringSquirrel(final SnickeringSquirrel card) { + super(card); + } + + @Override + public SnickeringSquirrel copy() { + return new SnickeringSquirrel(this); + } +} + +class SnickeringSquirrelEffect extends ReplacementEffectImpl { + + SnickeringSquirrelEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "You may tap {this} to increase the result of a die any player rolled by 1"; + } + + SnickeringSquirrelEffect(final SnickeringSquirrelEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + + if (controller != 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)) { + permanent.tap(game); + event.setAmount(event.getAmount() + 1); + } + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ROLL_DICE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public SnickeringSquirrelEffect copy() { + return new SnickeringSquirrelEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SquirrelPoweredScheme.java b/Mage.Sets/src/mage/cards/s/SquirrelPoweredScheme.java new file mode 100644 index 00000000000..26a4aefa594 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SquirrelPoweredScheme.java @@ -0,0 +1,102 @@ +/* + * 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.s; + +import java.util.UUID; +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.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author spjspj + */ +public class SquirrelPoweredScheme extends CardImpl { + + public SquirrelPoweredScheme(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Increase the result of each die you roll by 2. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SquirrelPoweredSchemeEffect())); + } + + public SquirrelPoweredScheme(final SquirrelPoweredScheme card) { + super(card); + } + + @Override + public SquirrelPoweredScheme copy() { + return new SquirrelPoweredScheme(this); + } +} + +class SquirrelPoweredSchemeEffect extends ReplacementEffectImpl { + + SquirrelPoweredSchemeEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Increase the result of each die you roll by 2"; + } + + SquirrelPoweredSchemeEffect(final SquirrelPoweredSchemeEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 2); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ROLL_DICE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public SquirrelPoweredSchemeEffect copy() { + return new SquirrelPoweredSchemeEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index c64225ccd3a..9d8a03c6d4b 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -60,6 +60,8 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class)); + cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class)); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java index 7af75c31292..8b3dfc5e738 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RollDiceEffect.java @@ -74,19 +74,8 @@ public class RollDiceEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject mageObject = game.getObject(source.getSourceId()); if (controller != null && mageObject != null) { - boolean result = true; - for (Effect effect : executingEffects) { - int rolled = controller.rollDice(game, numSides); - effect.setTargetPointer(this.targetPointer); - effect.setValue("rolled", rolled); - this.setValue("rolled", rolled); - if (effect instanceof OneShotEffect) { - result &= effect.apply(game, source); - } else { - game.addEffect((ContinuousEffect) effect, source); - } - } - return result; + controller.rollDice(game, numSides); + return true; } return false; } @@ -96,7 +85,7 @@ public class RollDiceEffect extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } - StringBuilder sb = new StringBuilder("Roll a " + numSides + " sided dice and then ").append(executingEffects.getText(mode)); + StringBuilder sb = new StringBuilder("Roll a " + numSides + " sided dice"); return sb.toString(); } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 2f1ac8b644e..1c10167deff 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -72,7 +72,7 @@ public enum SubType { BITH("Bith", SubTypeSet.CreatureType, true), // Star Wars BLINKMOTH("Blinkmoth", SubTypeSet.CreatureType), BOAR("Boar", SubTypeSet.CreatureType), - BRAINIAC("Brainiac", SubTypeSet.CreatureType), + BRAINIAC("Brainiac", SubTypeSet.CreatureType, true), // Unstable BRINGER("Bringer", SubTypeSet.CreatureType), BRUSHWAGG("Brushwagg", SubTypeSet.CreatureType), // C @@ -269,6 +269,7 @@ public enum SubType { SAPROLING("Saproling", SubTypeSet.CreatureType), SATYR("Satyr", SubTypeSet.CreatureType), SCARECROW("Scarecrow", SubTypeSet.CreatureType), + SCIENTIST("Scientist", SubTypeSet.CreatureType, true), // Unstable SCION("Scion", SubTypeSet.CreatureType), SCORPION("Scorpion", SubTypeSet.CreatureType), SCOUT("Scout", SubTypeSet.CreatureType), From 635c44614843bf4f8748ba0d71ddc2e9bcea8b0e Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 14 Dec 2017 23:44:18 +1100 Subject: [PATCH 34/49] Add 2 unstable cards. --- .../src/mage/cards/c/ChitteringDoom.java | 98 +++++++++++++ .../src/mage/cards/g/GarbageElemental.java | 115 +++++++++++++++ Mage.Sets/src/mage/cards/h/Hydradoodle.java | 133 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 3 + .../main/java/mage/players/PlayerImpl.java | 4 +- 5 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/c/ChitteringDoom.java create mode 100644 Mage.Sets/src/mage/cards/g/GarbageElemental.java create mode 100644 Mage.Sets/src/mage/cards/h/Hydradoodle.java diff --git a/Mage.Sets/src/mage/cards/c/ChitteringDoom.java b/Mage.Sets/src/mage/cards/c/ChitteringDoom.java new file mode 100644 index 00000000000..7152a7bdee1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChitteringDoom.java @@ -0,0 +1,98 @@ +/* + * 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.c; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenEffect; +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.permanent.token.SquirrelToken; + +/** + * + * @author spjspj + */ +public class ChitteringDoom extends CardImpl { + + public ChitteringDoom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + // Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token. + this.addAbility(new ChitteringDoomTriggeredAbility()); + } + + public ChitteringDoom(final ChitteringDoom card) { + super(card); + } + + @Override + public ChitteringDoom copy() { + return new ChitteringDoom(this); + } +} + +class ChitteringDoomTriggeredAbility extends TriggeredAbilityImpl { + + public ChitteringDoomTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new SquirrelToken()), false); + } + + public ChitteringDoomTriggeredAbility(final ChitteringDoomTriggeredAbility ability) { + super(ability); + } + + @Override + public ChitteringDoomTriggeredAbility copy() { + return new ChitteringDoomTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DICE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) { + if (event.getAmount() >= 4) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token"; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarbageElemental.java b/Mage.Sets/src/mage/cards/g/GarbageElemental.java new file mode 100644 index 00000000000..dcbe7c4a4c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarbageElemental.java @@ -0,0 +1,115 @@ +/* + * 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.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.BattleCryAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.game.Game; +import mage.game.permanent.token.GoblinToken; +import mage.game.permanent.token.Token; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class GarbageElemental extends CardImpl { + + public GarbageElemental(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add("Elemental"); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Battle cry + this.addAbility(new BattleCryAbility()); + + // When Garbage Elemental enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results. + this.addAbility(new EntersBattlefieldAbility(new GarbageElementalEffect(), + null, + "When {this} enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results", + null)); + + } + + public GarbageElemental(final GarbageElemental card) { + super(card); + } + + @Override + public GarbageElemental copy() { + return new GarbageElemental(this); + } +} + +class GarbageElementalEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("permanent with a counter"); + + static { + filter.add(new CounterPredicate(null)); + } + + GarbageElementalEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results"; + } + + GarbageElementalEffect(final GarbageElementalEffect effect) { + super(effect); + } + + @Override + public GarbageElementalEffect copy() { + return new GarbageElementalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int thisRoll = controller.rollDice(game, 6); + int thatRoll = controller.rollDice(game, 6); + + Token token = new GoblinToken(); + return token.putOntoBattlefield(Math.abs(thatRoll - thisRoll), game, source.getSourceId(), source.getControllerId()); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/h/Hydradoodle.java b/Mage.Sets/src/mage/cards/h/Hydradoodle.java new file mode 100644 index 00000000000..5637b38610c --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/Hydradoodle.java @@ -0,0 +1,133 @@ +/* + * 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.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.EntersBattlefieldEffect; +import mage.abilities.effects.OneShotEffect; +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.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.CounterPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class Hydradoodle extends CardImpl { + + public Hydradoodle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}{G}{G}"); + + this.subtype.add(SubType.HYDRA); + this.subtype.add(SubType.HOUND); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // As Hydradoodle enters the battlefield, roll X six-sided dice. Hydradoodle enters the battlefield with a number of +1/+1 counters on it equal to the total of those results. + this.addAbility(new EntersBattlefieldAbility(new HydradoodleEffect(), + null, + "As {this} enters the battlefield, roll X six-sided dice. {this} enters the battlefield with a number of +1/+1 counters on it equal to the total of those results", + null)); + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + } + + public Hydradoodle(final Hydradoodle card) { + super(card); + } + + @Override + public Hydradoodle copy() { + return new Hydradoodle(this); + } +} + +class HydradoodleEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("permanent with a counter"); + + static { + filter.add(new CounterPredicate(null)); + } + + HydradoodleEffect() { + super(Outcome.BoostCreature); + this.staticText = "roll X six-sided dice. {this} enters the battlefield with a number of +1/+1 counters on it equal to the total of those results"; + } + + HydradoodleEffect(final HydradoodleEffect effect) { + super(effect); + } + + @Override + public HydradoodleEffect copy() { + return new HydradoodleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (permanent != null) { + SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); + if (spellAbility != null + && spellAbility.getSourceId().equals(source.getSourceId()) + && permanent.getZoneChangeCounter(game) == spellAbility.getSourceObjectZoneChangeCounter()) { + int amount = spellAbility.getManaCostsToPay().getX(); + if (amount > 0) { + int total = 0; + for (int roll = 0; roll < amount; roll++) { + int thisRoll = controller.rollDice(game, 6); + total += thisRoll; + } + + permanent.addCounters(CounterType.P1P1.createInstance(total), source, game); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 9d8a03c6d4b..ea9eca2eb86 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -51,10 +51,13 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Amateur Auteur", 3, Rarity.COMMON, mage.cards.a.AmateurAuteur.class)); cards.add(new SetCardInfo("As Luck Would Have It", 102, Rarity.RARE, mage.cards.a.AsLuckWouldHaveIt.class)); cards.add(new SetCardInfo("Box of Free-Range Goblins", 77, Rarity.COMMON, mage.cards.b.BoxOfFreerangeGoblins.class)); + cards.add(new SetCardInfo("Chittering Doom", 104, Rarity.UNCOMMON, mage.cards.c.ChitteringDoom.class)); cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Garbage Elemental", 82, Rarity.UNCOMMON, mage.cards.g.GarbageElemental.class)); cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); + cards.add(new SetCardInfo("Hydradoodle", 112, Rarity.RARE, mage.cards.h.Hydradoodle.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index f16b7938ee3..c37fc4ab0df 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2352,9 +2352,9 @@ public abstract class PlayerImpl implements Player, Serializable { event.setAppliedEffects(appliedEffects); event.setAmount(result); if (!game.replaceEvent(event)) { - game.fireEvent(new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, result, event.getFlag())); + game.fireEvent(new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, event.getAmount(), event.getFlag())); } - return result; + return event.getAmount(); } @Override From aa4023562609055f31305dc099d44b7b0445b21e Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 15 Dec 2017 22:03:35 +1100 Subject: [PATCH 35/49] Add 1 unstable cards. --- Mage.Sets/src/mage/cards/l/LobeLobber.java | 116 +++++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 1 + 2 files changed, 117 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/l/LobeLobber.java diff --git a/Mage.Sets/src/mage/cards/l/LobeLobber.java b/Mage.Sets/src/mage/cards/l/LobeLobber.java new file mode 100644 index 00000000000..d720b96565b --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LobeLobber.java @@ -0,0 +1,116 @@ +/* + * 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.l; + +import java.util.UUID; +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.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +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.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; + +/** + * + * @author spjspj + */ +public class LobeLobber extends CardImpl { + + public LobeLobber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has "T: This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it." + Effect effect = new LobeLobberEffect(); + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + ability.addTarget(new TargetPlayer()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT))); + + // Equip 2 + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); + } + + public LobeLobber(final LobeLobber card) { + super(card); + } + + @Override + public LobeLobber copy() { + return new LobeLobber(this); + } +} + +class LobeLobberEffect extends OneShotEffect { + + public LobeLobberEffect() { + super(Outcome.Benefit); + this.staticText = "This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it"; + } + + public LobeLobberEffect(final LobeLobberEffect effect) { + super(effect); + } + + @Override + public LobeLobberEffect copy() { + return new LobeLobberEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent equipment = game.getPermanent(source.getSourceId()); + Player player = game.getPlayer(source.getFirstTarget()); + + if (controller != null && equipment != null && player != null) { + player.damage(1, source.getSourceId(), game, false, true); + int amount = controller.rollDice(game, 6); + if (amount >= 5) { + new UntapSourceEffect().apply(game, source); + } + return true; + } + + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index ea9eca2eb86..cc204b77c64 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -59,6 +59,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); cards.add(new SetCardInfo("Hydradoodle", 112, Rarity.RARE, mage.cards.h.Hydradoodle.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Lobe Lobber", 153, Rarity.UNCOMMON, mage.cards.l.LobeLobber.class)); cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); From f4cc88bf2a2f5b9d633d963ff4c19718c6a54e90 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 15 Dec 2017 22:04:42 +1100 Subject: [PATCH 36/49] Storm Crow --- Mage.Client/src/main/resources/card-pictures-tok.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 2bad012c0a2..7cd0c76905e 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -1062,6 +1062,7 @@ |Generate|TOK:USG|Minion|||MinionToken| |Generate|TOK:USG|Saproling|||SaprolingToken| |Generate|TOK:UST|Dragon|||DragonTokenGold| +|Generate|TOK:UST|StormCrow|||StormCrowToken| |Generate|TOK:V10|Wolf|||WolfToken| |Generate|TOK:V11|Faerie Rogue|||OonaQueenFaerieToken| |Generate|TOK:V12|Spirit|||SpiritToken| From 18b77741df3403ffe830570795919b5b001931ea Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 15 Dec 2017 23:23:17 +1100 Subject: [PATCH 37/49] Fix 1 UST card --- .../src/mage/cards/s/SwordOfDungeonsAndDragons.java | 9 +++++---- Utils/mtg-cards-data.txt | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SwordOfDungeonsAndDragons.java b/Mage.Sets/src/mage/cards/s/SwordOfDungeonsAndDragons.java index ab50290a067..191aa8226fb 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfDungeonsAndDragons.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfDungeonsAndDragons.java @@ -159,13 +159,14 @@ class SwordOfDungeonsAndDragonsEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int count = 1; - int dice = (int)(Math.random()*20+1); - while (dice == 20) { + int amount = controller.rollDice(game, 20); + + while (amount == 20) { count += 1; - dice = (int)(Math.random()*20+1); + amount = controller.rollDice(game, 20); } return new CreateTokenEffect(new DragonTokenGold(), count).apply(game, source); } return false; } -} \ No newline at end of file +} diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 51622f8aa3d..113a6e90685 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -32763,3 +32763,4 @@ Voracious Vacuum|Unstable|164|C|{3}|Host Creature - Construct|1|1|When this crea Wall of Fortune|Unstable|50|C|{1}{U}|Artifact Creature - Wall|0|4|Defender$You may tap an untapped Wall you control to have any player reroll a die that player rolled.| Wild Crocodile|Unstable|125|C|{1}{G}|Host Creature - Crocodile|1|1|When this creature enters the battlefield, search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.| Willing Test Subject|Unstable|126|C|{2}{G}|Creature- Spider Monkey Scientist|2|2|Reach$Whenever you roll a 4 or higher on a die, put a +1/+1 counter on Willing Test Subject.$6: Roll a six-sided die.| +Garbage Elemental|Unstable|82|U|{4}{R}|Creature - Elemental|3|2|Battle cry (Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn.)$When Garbage Elemental enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results.| From 667b2affdf54a3581d17aee04063b93414919473 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 16 Dec 2017 00:34:21 +1100 Subject: [PATCH 38/49] Add 2 unstable cards. --- .../src/mage/cards/k/KrarksOtherThumb.java | 123 ++++++++++++++ Mage.Sets/src/mage/cards/s/SteelSquirrel.java | 151 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + .../main/java/mage/players/PlayerImpl.java | 7 +- 4 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/k/KrarksOtherThumb.java create mode 100644 Mage.Sets/src/mage/cards/s/SteelSquirrel.java diff --git a/Mage.Sets/src/mage/cards/k/KrarksOtherThumb.java b/Mage.Sets/src/mage/cards/k/KrarksOtherThumb.java new file mode 100644 index 00000000000..b37ef0e660e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KrarksOtherThumb.java @@ -0,0 +1,123 @@ +/* + * 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 java.util.UUID; +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.constants.SuperType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.RandomUtil; + +/** + * + * @author spjspj + */ +public class KrarksOtherThumb extends CardImpl { + + public KrarksOtherThumb(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + addSuperType(SuperType.LEGENDARY); + + // If you would roll a die, instead roll two of those dice and ignore one of those results. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KrarksOtherThumbEffect())); + } + + public KrarksOtherThumb(final KrarksOtherThumb card) { + super(card); + } + + @Override + public KrarksOtherThumb copy() { + return new KrarksOtherThumb(this); + } +} + +class KrarksOtherThumbEffect extends ReplacementEffectImpl { + + KrarksOtherThumbEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would roll a die, instead roll two die and ignore one"; + } + + KrarksOtherThumbEffect(final KrarksOtherThumbEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if (player != null) { + // event.getData holds the num of sides of the die to roll + String data = event.getData(); + int numSides = Integer.parseInt(data); + int secondDieRoll = RandomUtil.nextInt(numSides) + 1; + + if (!game.isSimulation()) { + game.informPlayers("[Roll a die] " + player.getLogName() + " rolled a " + secondDieRoll); + } + if (player.chooseUse(outcome, "Ignore the first die roll?", source, game)) { + event.setAmount(secondDieRoll); + game.informPlayers(player.getLogName() + " ignores the first die roll."); + } else { + game.informPlayers(player.getLogName() + " ignores the second die roll."); + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ROLL_DICE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public KrarksOtherThumbEffect copy() { + return new KrarksOtherThumbEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteelSquirrel.java b/Mage.Sets/src/mage/cards/s/SteelSquirrel.java new file mode 100644 index 00000000000..9dd2a0e8be7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SteelSquirrel.java @@ -0,0 +1,151 @@ +/* + * 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.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RollDiceEffect; +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.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class SteelSquirrel extends CardImpl { + + public SteelSquirrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.SQUIRREL); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever you roll a 5 or higher on a die, Steel Squirrel gets +X/+X until end of turn, where X is the result. + this.addAbility(new SteelSquirrelTriggeredAbility()); + + // 6: Roll a six-sided die. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RollDiceEffect(null, 6), new GenericManaCost(6)); + this.addAbility(ability); + } + + public SteelSquirrel(final SteelSquirrel card) { + super(card); + } + + @Override + public SteelSquirrel copy() { + return new SteelSquirrel(this); + } +} + +class SteelSquirrelTriggeredAbility extends TriggeredAbilityImpl { + + public SteelSquirrelTriggeredAbility() { + super(Zone.BATTLEFIELD, new SteelSquirrelEffect()); + } + + public SteelSquirrelTriggeredAbility(final SteelSquirrelTriggeredAbility ability) { + super(ability); + } + + @Override + public SteelSquirrelTriggeredAbility copy() { + return new SteelSquirrelTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DICE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) { + if (event.getAmount() >= 5) { + for (Effect effect : this.getEffects()) { + effect.setValue("rolled", event.getAmount()); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you roll a 5 or higher on a die, " + super.getRule(); + } +} + +class SteelSquirrelEffect extends OneShotEffect { + + public SteelSquirrelEffect() { + super(Outcome.Benefit); + this.staticText = "{this} gets +X/+X until end of turn, where X is the result"; + } + + public SteelSquirrelEffect(final SteelSquirrelEffect effect) { + super(effect); + } + + @Override + public SteelSquirrelEffect copy() { + return new SteelSquirrelEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + if (this.getValue("rolled") != null) { + int rolled = (Integer) this.getValue("rolled"); + game.addEffect(new BoostSourceEffect(rolled, rolled, Duration.EndOfTurn), source); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index cc204b77c64..e7a07efbcec 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -59,6 +59,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); cards.add(new SetCardInfo("Hydradoodle", 112, Rarity.RARE, mage.cards.h.Hydradoodle.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Krark's Other Thumb", 151, Rarity.UNCOMMON, mage.cards.k.KrarksOtherThumb.class)); cards.add(new SetCardInfo("Lobe Lobber", 153, Rarity.UNCOMMON, mage.cards.l.LobeLobber.class)); cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); @@ -66,6 +67,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class)); cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class)); + cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class)); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index c37fc4ab0df..42fc10f5533 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2346,13 +2346,16 @@ public abstract class PlayerImpl implements Player, Serializable { public int rollDice(Game game, ArrayList appliedEffects, int numSides) { int result = RandomUtil.nextInt(numSides) + 1; if (!game.isSimulation()) { - game.informPlayers("[Roll a dice] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice"); + game.informPlayers("[Roll a die] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice"); } GameEvent event = new GameEvent(GameEvent.EventType.ROLL_DICE, playerId, null, playerId, result, true); event.setAppliedEffects(appliedEffects); event.setAmount(result); + event.setData(numSides + ""); if (!game.replaceEvent(event)) { - game.fireEvent(new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, event.getAmount(), event.getFlag())); + GameEvent ge = new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, event.getAmount(), event.getFlag()); + ge.setData(numSides + ""); + game.fireEvent(ge); } return event.getAmount(); } From 7a42ea416b0fc543626b64b37dd247180c007d94 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 16 Dec 2017 03:19:32 +0400 Subject: [PATCH 39/49] Fixed #4252 (errors on paths with $ symbol) --- .../src/main/java/org/mage/card/arcane/ManaSymbols.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index 6ff5ffd881a..a6903e42a1f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -721,14 +721,18 @@ public final class ManaSymbols { // not need to add different images (width and height do the work) // use best png size (generated on startup) TODO: add reload images after update String htmlImagesPath = getResourceSymbolsPath(ResourceSymbolSize.PNG); + htmlImagesPath = htmlImagesPath + .replace("$", "@S@"); // paths with $ will rise error, need escape that replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll( "$1$2 Date: Sat, 16 Dec 2017 03:45:38 +0400 Subject: [PATCH 40/49] Fixed #4253 (Scythe Specter throw NPE error on 0 cards in opponent hand) --- Mage.Sets/src/mage/cards/s/ScytheSpecter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java index 57d0405ef4d..c6854255185 100644 --- a/Mage.Sets/src/mage/cards/s/ScytheSpecter.java +++ b/Mage.Sets/src/mage/cards/s/ScytheSpecter.java @@ -129,7 +129,8 @@ class ScytheSpecterEffect extends OneShotEffect { } for (UUID playerId : game.getOpponents(controller.getId())) {//lose life equal to CMC - if (cardDiscarded.get(playerId).getConvertedManaCost() == highestCMC) { + Card card = cardDiscarded.get(playerId); + if ((card != null) && (card.getConvertedManaCost() == highestCMC)) { Player opponent = game.getPlayer(playerId); if (opponent != null && discardedCheck.get(playerId) == 1) {//check that card was discarded From b17d35dff3e5cab18708566295844baae615ece9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 16 Dec 2017 18:04:26 +1100 Subject: [PATCH 41/49] Add 1.25 UST cards --- .../src/mage/cards/e/EarlOfSquirrel.java | 102 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + .../keyword/SquirrellinkAbility.java | 66 ++++++++++++ .../src/main/java/mage/constants/SubType.java | 3 +- .../mage/game/permanent/PermanentImpl.java | 6 ++ .../main/java/mage/players/PlayerImpl.java | 12 ++- 6 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java create mode 100644 Mage/src/main/java/mage/abilities/keyword/SquirrellinkAbility.java diff --git a/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java b/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java new file mode 100644 index 00000000000..26ab0455af2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java @@ -0,0 +1,102 @@ +/* + * 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.e; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.SquirrellinkAbility; +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; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.util.SubTypeList; + +/** + * + * @author spjspj + */ +public class EarlOfSquirrel extends CardImpl { + + private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("Creature tokens you control"); + + static { + filter.add(new TokenPredicate()); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + private final static FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Other squirrels you control"); + + static { + filter2.add(new SubtypePredicate(SubType.SQUIRREL)); + filter2.add(new AnotherPredicate()); + filter2.add(new ControllerPredicate(TargetController.YOU)); + } + + public EarlOfSquirrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.SQUIRREL); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Squirrellink (Damage dealt by this creature also causes you to create that many 1/1 green Squirrel creature tokens.) + this.addAbility(SquirrellinkAbility.getInstance()); + + // Creature tokens you control are Squirrels in addition to their other creature types. + SubTypeList subTypes = new SubTypeList(); + subTypes.add(SubType.SQUIRREL); + Effect effect = new BecomesSubtypeAllEffect(Duration.WhileOnBattlefield, subTypes, filter, false); + effect.setText("Creature tokens you control are Squirrels in addition to their other creature types"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + // Other Squirrels you control get +1/+1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter2))); + } + + public EarlOfSquirrel(final EarlOfSquirrel card) { + super(card); + } + + @Override + public EarlOfSquirrel copy() { + return new EarlOfSquirrel(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index e7a07efbcec..fe31fd78449 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -53,6 +53,8 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Box of Free-Range Goblins", 77, Rarity.COMMON, mage.cards.b.BoxOfFreerangeGoblins.class)); cards.add(new SetCardInfo("Chittering Doom", 104, Rarity.UNCOMMON, mage.cards.c.ChitteringDoom.class)); cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); + cards.add(new SetCardInfo("Curious Killbot", 145, Rarity.COMMON, mage.cards.c.CuriousKillbot.class)); + cards.add(new SetCardInfo("Earl of Squirrel", 108, Rarity.RARE, mage.cards.e.EarlOfSquirrel.class)); cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Garbage Elemental", 82, Rarity.UNCOMMON, mage.cards.g.GarbageElemental.class)); cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); diff --git a/Mage/src/main/java/mage/abilities/keyword/SquirrellinkAbility.java b/Mage/src/main/java/mage/abilities/keyword/SquirrellinkAbility.java new file mode 100644 index 00000000000..a2c1f55e389 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/SquirrellinkAbility.java @@ -0,0 +1,66 @@ +/* +* 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; + +import mage.constants.Zone; +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; + +import java.io.ObjectStreamException; + +/** + * + * @author BetaSteward_at_googlemail.com (spjspj) + */ +public class SquirrellinkAbility extends StaticAbility implements MageSingleton { + + private static final SquirrellinkAbility instance = new SquirrellinkAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static SquirrellinkAbility getInstance() { + return instance; + } + + private SquirrellinkAbility() { + super(Zone.ALL, null); + } + + @Override + public String getRule() { + return "Squirrellink (Damage dealt by this creature also causes you to create that many 1/1/ green Squirrel creature tokens.)"; + } + + @Override + public SquirrellinkAbility copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 1c10167deff..4766d9114d4 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -178,7 +178,8 @@ public enum SubType { KALEESH("Kaleesh", SubTypeSet.CreatureType, true), // Star Wars KAVU("Kavu", SubTypeSet.CreatureType), KELDOR("KelDor", SubTypeSet.CreatureType, true), - KIRIN("Kirin", SubTypeSet.CreatureType), + KILLBOT("Killbot", SubTypeSet.CreatureType, true), // Unstable + KIRIN("Kirin", SubTypeSet.CreatureType), KITHKIN("Kithkin", SubTypeSet.CreatureType), KNIGHT("Knight", SubTypeSet.CreatureType), KOBOLD("Kobold", SubTypeSet.CreatureType), diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index c30051d825d..a5e5b9d59c1 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -52,6 +52,7 @@ import mage.game.combat.CombatGroup; import mage.game.command.CommandObject; import mage.game.events.*; import mage.game.events.GameEvent.EventType; +import mage.game.permanent.token.SquirrelToken; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; @@ -802,6 +803,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { if (dealtDamageByThisTurn == null) { dealtDamageByThisTurn = new HashSet<>(); } + // Unstable ability - Earl of Squirrel + if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { + Player player = game.getPlayer(sourceControllerId); + new SquirrelToken().putOntoBattlefield(damageDone, game, sourceId, player.getId()); + } dealtDamageByThisTurn.add(new MageObjectReference(source, game)); } if (source == null) { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 42fc10f5533..423aee4372d 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -80,6 +80,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; +import mage.game.permanent.token.SquirrelToken; import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.game.stack.StackObject; @@ -1841,6 +1842,11 @@ public abstract class PlayerImpl implements Player, Serializable { Player player = game.getPlayer(sourceControllerId); player.gainLife(actualDamage, game); } + // Unstable ability - Earl of Squirrel + if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) { + Player player = game.getPlayer(sourceControllerId); + new SquirrelToken().putOntoBattlefield(actualDamage, game, sourceId, player.getId()); + } game.fireEvent(new DamagedPlayerEvent(playerId, sourceId, playerId, actualDamage, combatDamage)); return actualDamage; } @@ -2312,7 +2318,7 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean flipCoin(Game game) { return this.flipCoin(game, null); } - + /** * @param game * @param appliedEffects @@ -2331,7 +2337,7 @@ public abstract class PlayerImpl implements Player, Serializable { } return event.getFlag(); } - + @Override public int rollDice(Game game, int numSides) { return this.rollDice(game, null, numSides); @@ -2344,7 +2350,7 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public int rollDice(Game game, ArrayList appliedEffects, int numSides) { - int result = RandomUtil.nextInt(numSides) + 1; + int result = RandomUtil.nextInt(numSides) + 1; if (!game.isSimulation()) { game.informPlayers("[Roll a die] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice"); } From b52d1d64b08940fce087201af3c66436f170e07a Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 16 Dec 2017 20:05:05 +1100 Subject: [PATCH 42/49] Add 1.25 UST cards --- .../src/mage/cards/c/CuriousKillbot.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CuriousKillbot.java diff --git a/Mage.Sets/src/mage/cards/c/CuriousKillbot.java b/Mage.Sets/src/mage/cards/c/CuriousKillbot.java new file mode 100644 index 00000000000..692c666743c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CuriousKillbot.java @@ -0,0 +1,59 @@ +/* + * 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.c; + +import java.util.UUID; +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author spjspj + */ +public class CuriousKillbot extends CardImpl { + + public CuriousKillbot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.KILLBOT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + } + + public CuriousKillbot(final CuriousKillbot card) { + super(card); + } + + @Override + public CuriousKillbot copy() { + return new CuriousKillbot(this); + } +} From b790c03b677b13d7a7fc55a8242ccd4856d5eafa Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 16 Dec 2017 22:29:55 +1100 Subject: [PATCH 43/49] Add 1 UST cards --- .../src/mage/cards/b/BuzzingWhackADoodle.java | 217 ++++++++++++++++++ .../src/mage/cards/e/EarlOfSquirrel.java | 11 +- Mage.Sets/src/mage/sets/Unstable.java | 1 + 3 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/b/BuzzingWhackADoodle.java diff --git a/Mage.Sets/src/mage/cards/b/BuzzingWhackADoodle.java b/Mage.Sets/src/mage/cards/b/BuzzingWhackADoodle.java new file mode 100644 index 00000000000..45263884e9b --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BuzzingWhackADoodle.java @@ -0,0 +1,217 @@ +/* + * 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.b; + +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.IntCompareCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +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.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPlayer; +import mage.target.common.TargetOpponent; + +/** + * + * @author spjspj + */ +public class BuzzingWhackADoodle extends CardImpl { + + public BuzzingWhackADoodle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // As Buzzing Whack-a-Doodle enters the battlefield, you and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, Buzzing Whack-a-Doodle has that ability. Otherwise it has Buzz. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BuzzingWhackADoodleEffect(), false)); + + // *Whack - T: Target player loses 2 life. + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(2), new TapSourceCost(), new WhackCondition()); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + // *Doodle - T: You gain 3 life. + Ability ability2 = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(3), new TapSourceCost(), new DoodleCondition()); + this.addAbility(ability2); + + // *Buzz - 2, T: Draw a card. + Ability ability3 = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{2}"), new BuzzCondition()); + ability3.addCost(new TapSourceCost()); + this.addAbility(ability3); + } + + public BuzzingWhackADoodle(final BuzzingWhackADoodle card) { + super(card); + } + + @Override + public BuzzingWhackADoodle copy() { + return new BuzzingWhackADoodle(this); + } +} + +class BuzzingWhackADoodleEffect extends OneShotEffect { + + BuzzingWhackADoodleEffect() { + super(Outcome.Benefit); + this.staticText = "You and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, {this} has that ability. Otherwise it has Buzz"; + } + + BuzzingWhackADoodleEffect(final BuzzingWhackADoodleEffect effect) { + super(effect); + } + + @Override + public BuzzingWhackADoodleEffect copy() { + return new BuzzingWhackADoodleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int whackCount = 0; + int doodleCount = 0; + + if (controller.chooseUse(Outcome.Benefit, "Choose Whack (yes) or Doodle (no)?", source, game)) { + whackCount++; + } else { + doodleCount++; + } + + 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 choose Whack or Doodle"); + } + } + + if (opponent != null) { + if (opponent.chooseUse(Outcome.Benefit, "Choose Whack (yes) or Doodle (no)?", source, game)) { + whackCount++; + } else { + doodleCount++; + } + } + } + + if (whackCount == 2) { + game.informPlayers("Whack was chosen"); + game.getState().setValue("whack" + source.getSourceId(), Boolean.TRUE); + } else if (doodleCount == 2) { + game.informPlayers("Doodle was chosen"); + game.getState().setValue("doodle" + source.getSourceId(), Boolean.TRUE); + } else { + game.informPlayers("Buzz was chosen"); + game.getState().setValue("buzz" + source.getSourceId(), Boolean.TRUE); + } + return true; + } + return false; + } +} + +class WhackCondition extends IntCompareCondition { + + WhackCondition() { + super(ComparisonType.MORE_THAN, 0); + } + + @Override + protected int getInputValue(Game game, Ability source) { + Object object = game.getState().getValue("whack" + source.getSourceId()); + if (object != null && object instanceof Boolean && (Boolean) object) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return "if both players picked 'Whack'"; + } +} + +class DoodleCondition extends IntCompareCondition { + + DoodleCondition() { + super(ComparisonType.MORE_THAN, 0); + } + + @Override + protected int getInputValue(Game game, Ability source) { + Object object = game.getState().getValue("doodle" + source.getSourceId()); + if (object != null && object instanceof Boolean && (Boolean) object) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return "if both players picked 'Doodle'"; + } +} + +class BuzzCondition extends IntCompareCondition { + + BuzzCondition() { + super(ComparisonType.MORE_THAN, 0); + } + + @Override + protected int getInputValue(Game game, Ability source) { + Object object = game.getState().getValue("buzz" + source.getSourceId()); + if (object != null && object instanceof Boolean && (Boolean) object) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return "if both players picked differently"; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java b/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java index 26ab0455af2..661c498d401 100644 --- a/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java +++ b/Mage.Sets/src/mage/cards/e/EarlOfSquirrel.java @@ -55,18 +55,13 @@ import mage.util.SubTypeList; public class EarlOfSquirrel extends CardImpl { private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("Creature tokens you control"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Other squirrels you control"); + static { filter.add(new TokenPredicate()); filter.add(new ControllerPredicate(TargetController.YOU)); - } - - private final static FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Other squirrels you control"); - - static { filter2.add(new SubtypePredicate(SubType.SQUIRREL)); - filter2.add(new AnotherPredicate()); - filter2.add(new ControllerPredicate(TargetController.YOU)); } public EarlOfSquirrel(UUID ownerId, CardSetInfo setInfo) { @@ -88,7 +83,7 @@ public class EarlOfSquirrel extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // Other Squirrels you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter2))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter2, true))); } public EarlOfSquirrel(final EarlOfSquirrel card) { diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index fe31fd78449..7deb413631f 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -51,6 +51,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Amateur Auteur", 3, Rarity.COMMON, mage.cards.a.AmateurAuteur.class)); cards.add(new SetCardInfo("As Luck Would Have It", 102, Rarity.RARE, mage.cards.a.AsLuckWouldHaveIt.class)); cards.add(new SetCardInfo("Box of Free-Range Goblins", 77, Rarity.COMMON, mage.cards.b.BoxOfFreerangeGoblins.class)); + cards.add(new SetCardInfo("Buzzing Whack-a-Doodle", 141, Rarity.UNCOMMON, mage.cards.b.BuzzingWhackADoodle.class)); cards.add(new SetCardInfo("Chittering Doom", 104, Rarity.UNCOMMON, mage.cards.c.ChitteringDoom.class)); cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class)); cards.add(new SetCardInfo("Curious Killbot", 145, Rarity.COMMON, mage.cards.c.CuriousKillbot.class)); From e3a61e2c0b32a13ef37c668f4b773d4ce486bf4b Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 16 Dec 2017 23:48:22 +1100 Subject: [PATCH 44/49] Add 1 UST cards --- Mage.Sets/src/mage/cards/i/Inhumaniac.java | 106 +++++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 1 + 2 files changed, 107 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/Inhumaniac.java diff --git a/Mage.Sets/src/mage/cards/i/Inhumaniac.java b/Mage.Sets/src/mage/cards/i/Inhumaniac.java new file mode 100644 index 00000000000..94b94efb5f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/Inhumaniac.java @@ -0,0 +1,106 @@ +/* + * 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.i; + +import java.util.UUID; +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.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author spjspj + */ +public class Inhumaniac extends CardImpl { + + public Inhumaniac(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.BRAINIAC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // At the beginning of your upkeep, roll a six-sided die. On a 3 or 4, put a +1/+1 counter on Inhumaniac. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from Inhumaniac. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new InhumaniacEffect(), TargetController.YOU, false)); + } + + public Inhumaniac(final Inhumaniac card) { + super(card); + } + + @Override + public Inhumaniac copy() { + return new Inhumaniac(this); + } +} + +class InhumaniacEffect extends OneShotEffect { + + public InhumaniacEffect() { + super(Outcome.Benefit); + this.staticText = "roll a six-sided die. On a 3 or 4, put a +1/+1 counter on {this}. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from {this}"; + } + + public InhumaniacEffect(final InhumaniacEffect effect) { + super(effect); + } + + @Override + public InhumaniacEffect copy() { + return new InhumaniacEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller != null && permanent != null) { + int amount = controller.rollDice(game, 6); + if (amount >= 3 && amount <= 4) { + permanent.addCounters(CounterType.P1P1.createInstance(1), source, game); + } else if (amount >= 5) { + permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); + } else if (amount == 1) { + permanent.getCounters(game).removeAllCounters(CounterType.P1P1); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 7deb413631f..4285aabfc41 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -61,6 +61,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); cards.add(new SetCardInfo("Hydradoodle", 112, Rarity.RARE, mage.cards.h.Hydradoodle.class)); + cards.add(new SetCardInfo("Inhumaniac", 59, Rarity.UNCOMMON, mage.cards.i.Inhumaniac.class)); cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Krark's Other Thumb", 151, Rarity.UNCOMMON, mage.cards.k.KrarksOtherThumb.class)); cards.add(new SetCardInfo("Lobe Lobber", 153, Rarity.UNCOMMON, mage.cards.l.LobeLobber.class)); From 36055178bd855d51fded71cb88177cea5654bf15 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 16 Dec 2017 19:58:11 +0400 Subject: [PATCH 45/49] - fixed UST - Sword of Dungeons card name; - fixed mage-verify tests for UST set; --- Mage.Sets/src/mage/sets/Unstable.java | 2 +- .../java/mage/verify/VerifyCardDataTest.java | 50 +++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 4285aabfc41..f43e3683377 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -73,7 +73,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class)); cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class)); cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); - cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); + cards.add(new SetCardInfo("Sword of Dungeons & Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); cards.add(new SetCardInfo("Willing Test Subject", 126, Rarity.COMMON, mage.cards.w.WillingTestSubject.class)); } diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index a0b1a113141..574e5b627fc 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -22,6 +22,35 @@ public class VerifyCardDataTest { // right now this is very noisy, and not useful enough to make any assertions on private static final boolean CHECK_SOURCE_TOKENS = false; + private static final HashMap> skipCheckLists = new HashMap<>(); + private static void skipListCreate(String listName){ skipCheckLists.put(listName, new LinkedHashSet<>()); } + private static void skipListAddName(String listName, String name){ skipCheckLists.get(listName).add(name); } + private static boolean skipListHaveName(String listName, String name){ return skipCheckLists.get(listName).contains(name); } + + static { + // skip lists for checks (example: ustable cards with same name may have different stats) + + // power-toughness + skipListCreate("PT"); + skipListAddName("PT", "Garbage Elemental"); // UST + + // color + skipListCreate("COLOR"); + //skipListAddName("COLOR", "Ulrich, Uncontested Alpha"); // gatherer is missing the color indicator on one card and json has wrong data (16.12.2017: not actual) + + // cost + skipListCreate("COST"); + + // supertype + skipListCreate("SUPERTYPE"); + + // type + skipListCreate("TYPE"); + + // subtype + skipListCreate("SUBTYPE"); + } + public static List allCards() { Collection sets = Sets.getInstance().values(); List cards = new ArrayList<>(); @@ -38,7 +67,7 @@ public class VerifyCardDataTest { } private void warn(Card card, String message) { - System.out.println("Warning: " + message + " for " + card.getName()); + System.out.println("Warning: " + message + " for " + card.getName() + " (" + card.getExpansionSetCode() + ")"); } private void fail(Card card, String category, String message) { @@ -129,10 +158,8 @@ public class VerifyCardDataTest { } private void checkColors(Card card, JsonCard ref) { - // gatherer is missing the color indicator on one card: - if ("Ulrich, Uncontested Alpha".equals(ref.name)) { - return; - } + if (skipListHaveName("COLOR", card.getName())){ return; } + Collection expected = ref.colors; ObjectColor color = card.getColor(null); if (expected == null) { @@ -149,7 +176,11 @@ public class VerifyCardDataTest { } private void checkSubtypes(Card card, JsonCard ref) { + if (skipListHaveName("SUBTYPE", card.getName())){ return; } + Collection expected = ref.subtypes; + + // fix names (e.g. Urza’s to Urza's) if (expected != null && expected.contains("Urza’s")) { expected = new ArrayList<>(expected); for (ListIterator it = ((List) expected).listIterator(); it.hasNext();) { @@ -158,12 +189,15 @@ public class VerifyCardDataTest { } } } + if (!eqSet(card.getSubtype(null).stream().map(p -> p.toString()).collect(Collectors.toSet()), expected)) { fail(card, "subtypes", card.getSubtype(null) + " != " + expected); } } private void checkSupertypes(Card card, JsonCard ref) { + if (skipListHaveName("SUPERTYPE", card.getName())){ return; } + Collection expected = ref.supertypes; if (!eqSet(card.getSuperType().stream().map(s -> s.toString()).collect(Collectors.toList()), expected)) { fail(card, "supertypes", card.getSuperType() + " != " + expected); @@ -171,6 +205,8 @@ public class VerifyCardDataTest { } private void checkTypes(Card card, JsonCard ref) { + if (skipListHaveName("TYPE", card.getName())){ return; } + Collection expected = ref.types; List type = new ArrayList<>(); for (CardType cardType : card.getCardType()) { @@ -189,6 +225,8 @@ public class VerifyCardDataTest { } private void checkPT(Card card, JsonCard ref) { + if (skipListHaveName("PT", card.getName())){ return; } + if (!eqPT(card.getPower().toString(), ref.power) || !eqPT(card.getToughness().toString(), ref.toughness)) { String pt = card.getPower() + "/" + card.getToughness(); String expected = ref.power + '/' + ref.toughness; @@ -205,6 +243,8 @@ public class VerifyCardDataTest { } private void checkCost(Card card, JsonCard ref) { + if (skipListHaveName("COST", card.getName())){ return; } + String expected = ref.manaCost; String cost = join(card.getManaCost().getSymbols()); if (cost != null && cost.isEmpty()) { From 7d86fe2d7608aa1242371d9110bee2035b15b805 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sat, 16 Dec 2017 19:32:34 +0100 Subject: [PATCH 46/49] Fixed Nemesis image downloads from MythicSpoiler (fixes #4260) --- .../org/mage/plugins/card/dl/sources/MythicspoilerComSource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java index 2a468eabc8e..d29a7d113d8 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java @@ -259,6 +259,7 @@ public enum MythicspoilerComSource implements CardImageSource { setsAliases = new HashMap<>(); setsAliases.put("exp", "bfz"); setsAliases.put("xln", "ixa"); + setsAliases.put("nem", "nms"); cardNameAliases = new HashMap<>(); // set+wrong name from web side => correct card name cardNameAliases.put("MM2-otherwordlyjourney", "otherworldlyjourney"); From bbee401ef39369a527f73a5916fa3e759408866b Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Sun, 17 Dec 2017 00:33:55 +0100 Subject: [PATCH 47/49] Fix for erratic Haunting Wind behavior (fixes #4186) --- Mage.Sets/src/mage/cards/h/HauntingWind.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HauntingWind.java b/Mage.Sets/src/mage/cards/h/HauntingWind.java index 4533661fbb0..dca80cded44 100644 --- a/Mage.Sets/src/mage/cards/h/HauntingWind.java +++ b/Mage.Sets/src/mage/cards/h/HauntingWind.java @@ -29,8 +29,6 @@ package mage.cards.h; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; @@ -98,14 +96,8 @@ class HauntingWindTriggeredAbility extends TriggeredAbilityImpl { if (stackAbility == null) { return false; } - boolean triggerable = true; - for (Cost cost : stackAbility.getCosts()) { - if (cost instanceof TapSourceCost) { - triggerable = false; - break; - } - } - if (!triggerable) { + String abilityText = stackAbility.getRule(true); + if (abilityText.contains("{T}:") || abilityText.contains("{T},") || abilityText.contains("{T} or")) { return false; } for (Effect effect : this.getEffects()) { From 710cd3ade2da2de109624d95195d62498ba505c7 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 17 Dec 2017 17:14:04 +1100 Subject: [PATCH 48/49] Add 2 UST cards --- Mage.Sets/src/mage/cards/g/GOTOJAIL.java | 193 ++++++++++++++++++ .../src/mage/cards/s/SnickeringSquirrel.java | 2 +- Mage.Sets/src/mage/cards/t/TimeOut.java | 134 ++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 2 + 4 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/g/GOTOJAIL.java create mode 100644 Mage.Sets/src/mage/cards/t/TimeOut.java diff --git a/Mage.Sets/src/mage/cards/g/GOTOJAIL.java b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java new file mode 100644 index 00000000000..24f03f27b61 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java @@ -0,0 +1,193 @@ +/* + * 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.g; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseOpponentEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +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.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +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.TargetCreaturePermanent; +import mage.util.CardUtil; + +/** + * + * @author spjspj + */ +public class GOTOJAIL extends CardImpl { + + private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); + + static { + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + } + + public GOTOJAIL(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + // When GO TO JAIL enters the battlefield, exile target creature an opponent controls until GO TO JAIL leaves the battlefield. + Ability ability = new EntersBattlefieldTriggeredAbility(new GoToJailExileEffect()); + ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); + this.addAbility(ability); + + // At the beginning of the upkeep of the exiled card's owner, that player rolls two six-sided dice. If he or she rolls doubles, sacrifice GO TO JAIL. + this.addAbility(new GoToJailTriggeredAbility()); + } + + public GOTOJAIL(final GOTOJAIL card) { + super(card); + } + + @Override + public GOTOJAIL copy() { + return new GOTOJAIL(this); + } +} + +class GoToJailExileEffect extends OneShotEffect { + + public GoToJailExileEffect() { + super(Outcome.Benefit); + this.staticText = "exile target creature an opponent controls until {this} leaves the battlefield."; + } + + public GoToJailExileEffect(final GoToJailExileEffect effect) { + super(effect); + } + + @Override + public GoToJailExileEffect copy() { + return new GoToJailExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); + Permanent targetPermanent = game.getPermanent(targetPointer.getFirst(game, source)); + + // If GO TO JAIL leaves the battlefield before its triggered ability resolves, + // the target creature won't be exiled. + if (permanent != null && targetPermanent != null) { + Player controller = game.getPlayer(targetPermanent.getControllerId()); + if (controller != null) { + game.getState().setValue(permanent.getId() + ChooseOpponentEffect.VALUE_KEY, controller.getId()); + return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName()).apply(game, source); + } + } + return false; + } +} + +class GoToJailTriggeredAbility extends TriggeredAbilityImpl { + + public GoToJailTriggeredAbility() { + super(Zone.BATTLEFIELD, new GoToJailUpkeepEffect(), false); + } + + public GoToJailTriggeredAbility(final GoToJailTriggeredAbility ability) { + super(ability); + } + + @Override + public GoToJailTriggeredAbility copy() { + return new GoToJailTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.UPKEEP_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals((UUID) game.getState().getValue(this.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY)); + } + + @Override + public String getRule() { + return "At the beginning of the chosen player's upkeep, " + super.getRule(); + } +} + +class GoToJailUpkeepEffect extends OneShotEffect { + + public GoToJailUpkeepEffect() { + super(Outcome.Sacrifice); + this.staticText = "that player rolls two six-sided dice. If he or she rolls doubles, sacrifice {this}"; + } + + public GoToJailUpkeepEffect(final GoToJailUpkeepEffect effect) { + super(effect); + } + + @Override + public GoToJailUpkeepEffect copy() { + return new GoToJailUpkeepEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); + + + if (sourceObject != null && sourceObject instanceof Permanent && permanent != null) { + UUID opponentId = (UUID) game.getState().getValue(sourceObject.getId().toString() + ChooseOpponentEffect.VALUE_KEY); + Player opponent = game.getPlayer(opponentId); + + if (opponent != null) { + int thisRoll = opponent.rollDice(game, 6); + int thatRoll = opponent.rollDice(game, 6); + if (thisRoll == thatRoll) { + return permanent.sacrifice(source.getSourceId(), game); + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java index e0ff21a6e86..7022dc9c0bb 100644 --- a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java +++ b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java @@ -105,7 +105,7 @@ class SnickeringSquirrelEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return source.getControllerId().equals(event.getPlayerId()); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TimeOut.java b/Mage.Sets/src/mage/cards/t/TimeOut.java new file mode 100644 index 00000000000..2907d764fce --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TimeOut.java @@ -0,0 +1,134 @@ +/* + * 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.t; + +import java.util.Deque; +import java.util.LinkedList; +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.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * + * @author spjspj + */ +public class TimeOut extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("nonland permanent"); + + static { + filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + } + + public TimeOut(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}"); + + // Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result. + this.getSpellAbility().addEffect(new TimeOutEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + } + + public TimeOut(final TimeOut card) { + super(card); + } + + @Override + public TimeOut copy() { + return new TimeOut(this); + } +} + +class TimeOutEffect extends OneShotEffect { + + public TimeOutEffect() { + super(Outcome.Benefit); + this.staticText = "Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result"; + } + + public TimeOutEffect(final TimeOutEffect effect) { + super(effect); + } + + @Override + public TimeOutEffect copy() { + return new TimeOutEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (permanent != null) { + Player owner = game.getPlayer(permanent.getOwnerId()); + if (owner != null) { + int amount = controller.rollDice(game, 6); + Cards cards = new CardsImpl(); + Deque cardIds = new LinkedList<>(); + for (int i = 0; i < amount; i++) { + Card card = owner.getLibrary().removeFromTop(game); + cards.add(card); + cardIds.push(card.getId()); + } + // return cards back to library + game.informPlayers(new StringBuilder(controller.getLogName()) + .append(" puts ").append(permanent.getName()) + .append(" beneath the top ").append(amount) + .append(" cards of ").append(owner.getLogName()).append("'s library").toString()); + permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + while (!cardIds.isEmpty()) { + UUID cardId = cardIds.poll(); + Card card = cards.get(cardId, game); + if (card != null) { + card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); + } + } + return true; + } + } + } + + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 4285aabfc41..4f6b6fc5bad 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -68,6 +68,8 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); + cards.add(new SetCardInfo("GO TO JAIL", 8, Rarity.COMMON, mage.cards.g.GOTOJAIL.class)); + cards.add(new SetCardInfo("Time Out", 48, Rarity.COMMON, mage.cards.t.TimeOut.class)); cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class)); cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class)); From 578ea668bbccb1520296ebaea33fb2d6d473ebc8 Mon Sep 17 00:00:00 2001 From: spjspj Date: Mon, 18 Dec 2017 00:28:37 +1100 Subject: [PATCH 49/49] Add Summon The Pack (modification - needs the creatures to be zombies (done) but not black) --- Mage.Sets/src/mage/cards/s/SummonThePack.java | 139 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unstable.java | 5 +- .../common/ChooseExpansionSetEffect.java | 102 +++++++++++++ Mage/src/main/java/mage/cards/CardImpl.java | 3 + 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SummonThePack.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/ChooseExpansionSetEffect.java diff --git a/Mage.Sets/src/mage/cards/s/SummonThePack.java b/Mage.Sets/src/mage/cards/s/SummonThePack.java new file mode 100644 index 00000000000..445e3dc47d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonThePack.java @@ -0,0 +1,139 @@ +/* + * 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.s; + +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.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseExpansionSetEffect; +import mage.abilities.effects.common.continuous.BecomesBlackZombieAdditionEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author spjspj + */ +public class SummonThePack extends CardImpl { + + public SummonThePack(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{B}"); + + // Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types. (Remove those cards from your deck before beginning a new game) + this.getSpellAbility().addEffect(new SummonThePackEffect()); + } + + public SummonThePack(final SummonThePack card) { + super(card); + } + + @Override + public SummonThePack copy() { + return new SummonThePack(this); + } +} + +class SummonThePackEffect extends OneShotEffect { + + public SummonThePackEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types"; + } + + public SummonThePackEffect(final SummonThePackEffect effect) { + super(effect); + } + + @Override + public SummonThePackEffect copy() { + return new SummonThePackEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChooseExpansionSetEffect effect = new ChooseExpansionSetEffect(Outcome.UnboostCreature); + effect.apply(game, source); + Player controller = game.getPlayer(source.getControllerId()); + + String setChosen = null; + if (effect.getValue("setchosen") != null) { + setChosen = (String) effect.getValue("setchosen"); + } else if (game.getState().getValue(this.getId() + "_set") != null) { + setChosen = (String) game.getState().getValue(this.getId() + "_set"); + } + + if (setChosen != null && controller != null) { + //ExpansionInfo set = ExpansionRepository.instance.getSetByName(setChosen); + ExpansionSet expansionSet = Sets.findSet(setChosen); + if (expansionSet != null) { + List boosterPack = expansionSet.create15CardBooster(); + List creatureCards = new ArrayList<>(); + + if (boosterPack != null) { + StringBuilder message = new StringBuilder(controller.getLogName()).append(" opened: "); + + for (Card c : boosterPack) { + message.append(c.getName()).append(" "); + if (c != null && c.isCreature()) { + message.append(" (creature card) "); + ContinuousEffect effect2 = new BecomesBlackZombieAdditionEffect(); + effect2.setTargetPointer(new FixedTarget(c.getId())); + game.addEffect(effect2, source); + creatureCards.add(c); + c.setZone(Zone.OUTSIDE, game); + } + } + + if (creatureCards.size() > 0) { + Set ccs = new HashSet(creatureCards); + game.loadCards(ccs, controller.getId()); + controller.moveCards(ccs, Zone.BATTLEFIELD, source, game); + } + + game.informPlayers(message.toString()); + } + } + } + + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unstable.java b/Mage.Sets/src/mage/sets/Unstable.java index 4f6b6fc5bad..8c7a1aa28c2 100644 --- a/Mage.Sets/src/mage/sets/Unstable.java +++ b/Mage.Sets/src/mage/sets/Unstable.java @@ -57,6 +57,7 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Curious Killbot", 145, Rarity.COMMON, mage.cards.c.CuriousKillbot.class)); cards.add(new SetCardInfo("Earl of Squirrel", 108, Rarity.RARE, mage.cards.e.EarlOfSquirrel.class)); cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("GO TO JAIL", 8, Rarity.COMMON, mage.cards.g.GOTOJAIL.class)); cards.add(new SetCardInfo("Garbage Elemental", 82, Rarity.UNCOMMON, mage.cards.g.GarbageElemental.class)); cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class)); cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class)); @@ -68,15 +69,15 @@ public class Unstable extends ExpansionSet { cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class)); cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class)); - cards.add(new SetCardInfo("GO TO JAIL", 8, Rarity.COMMON, mage.cards.g.GOTOJAIL.class)); - cards.add(new SetCardInfo("Time Out", 48, Rarity.COMMON, mage.cards.t.TimeOut.class)); cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class)); cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.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("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class)); cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class)); + cards.add(new SetCardInfo("Time Out", 48, Rarity.COMMON, mage.cards.t.TimeOut.class)); cards.add(new SetCardInfo("Willing Test Subject", 126, Rarity.COMMON, mage.cards.w.WillingTestSubject.class)); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseExpansionSetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseExpansionSetEffect.java new file mode 100644 index 00000000000..ba2a223a87a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseExpansionSetEffect.java @@ -0,0 +1,102 @@ +/* + * 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.effects.common; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.repository.ExpansionRepository; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author LevelX2 (spjspj) + */ +public class ChooseExpansionSetEffect extends OneShotEffect { + + public ChooseExpansionSetEffect(Outcome outcome) { + super(outcome); + staticText = "choose an expansion set"; + } + + public ChooseExpansionSetEffect(final ChooseExpansionSetEffect 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) { + Choice setChoice = new ChoiceImpl(true); + setChoice.setMessage("Choose expansion set"); + List setCodes = ExpansionRepository.instance.getSetCodes(); + Set sets = new HashSet(setCodes); + + setChoice.setChoices(sets); + + while (!controller.choose(outcome, setChoice, game)) { + if (!controller.canRespond()) { + return false; + } + } + if (setChoice.getChoice() == null) { + return false; + } + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " has chosen set " + setChoice.getChoice()); + } + game.getState().setValue(mageObject.getId() + "_set", setChoice.getChoice()); + this.setValue("setchosen", setChoice.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen set", CardUtil.addToolTipMarkTags("Chosen set: " + setChoice.getChoice()), game); + } + } + return false; + } + + @Override + public ChooseExpansionSetEffect copy() { + return new ChooseExpansionSetEffect(this); + } + +} diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 22361e38d8d..d2f8d1a2300 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -594,6 +594,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } else if (game.getPhase() == null) { // E.g. Commander of commander game removed = true; + } else { + // Unstable - Summon the Pack + removed = true; } break; case BATTLEFIELD: // for sacrificing permanents or putting to library