From 79be2c4ce801b3bd6e60d9e3a79b809a01de0378 Mon Sep 17 00:00:00 2001 From: Achilles <jeff@delmarus.com> Date: Sun, 16 Jul 2017 12:19:59 -0500 Subject: [PATCH 1/7] - Little refactor CardsCycledOrDiscardedThisTurnWatcher --- .../common/CardsCycledOrDiscardedThisTurnWatcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java index 5ed5bc021d..3dc715174a 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsCycledOrDiscardedThisTurnWatcher.java @@ -61,7 +61,9 @@ public class CardsCycledOrDiscardedThisTurnWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DISCARDED_CARD && event.getPlayerId() != null) { + if (event.getType() == GameEvent.EventType.DISCARDED_CARD + || event.getType() == GameEvent.EventType.CYCLED_CARD + && event.getPlayerId() != null) { Card card = game.getCard(event.getTargetId()); if (card != null) { Cards c = getCardsCycledOrDiscardedThisTurn(event.getPlayerId()); From ccc4c00aceb4c0bf43ce3b6f789a94e4678d7eab Mon Sep 17 00:00:00 2001 From: LevelX2 <ludwig.hirth@online.de> Date: Sun, 16 Jul 2017 19:25:33 +0200 Subject: [PATCH 2/7] * Flameshadow Conjuring - Fixed that the {R} could not be paid during resolution (fixes #3559). --- .../src/mage/cards/f/FlameshadowConjuring.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java index d2e69c4671..7b2f75d20c 100644 --- a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java +++ b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java @@ -27,12 +27,14 @@ */ package mage.cards.f; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; @@ -48,8 +50,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; -import java.util.UUID; - /** * * @author fireshoes @@ -63,12 +63,14 @@ public class FlameshadowConjuring extends CardImpl { } public FlameshadowConjuring(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); // Whenever a nontoken creature enters the battlefield under your control, you may pay {R}. If you do, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step. - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new FlameshadowConjuringEffect(), filterNontoken, false, SetTargetPointer.PERMANENT, - "Whenever a nontoken creature enters the battlefield under your control, you may pay {R}. If you do, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step"); - ability.addCost(new ManaCostsImpl("{R}")); + Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid( + new FlameshadowConjuringEffect(), new ManaCostsImpl("{R}"), "Pay {R} to create a token that's a copy of that creature that entered the battlefield?"), filterNontoken, false, SetTargetPointer.PERMANENT, + "Whenever a nontoken creature enters the battlefield under your control, " + + "you may pay {R}. If you do, create a token that's a copy of that creature. " + + "That token gains haste. Exile it at the beginning of the next end step"); this.addAbility(ability); } From 5774deb90c16d617c2db28611540f9706c115ca2 Mon Sep 17 00:00:00 2001 From: LevelX2 <ludwig.hirth@online.de> Date: Sun, 16 Jul 2017 19:46:41 +0200 Subject: [PATCH 3/7] XMAGE 1.4.24V3 --- 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 7211230461..7c567df7b2 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<MageVersion> { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 24; - public final static String MAGE_VERSION_MINOR_PATCH = "V2"; + public final static String MAGE_VERSION_MINOR_PATCH = "V3"; 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 432120948a..947ef323cf 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 = 84; + private static final long CARD_CONTENT_VERSION = 85; private final TreeSet<String> landTypes = new TreeSet<>(); private Dao<CardInfo, Object> cardDao; private Set<String> classNames; From fd1522a42880a6add0a166a0c71f28e2f3e9619b Mon Sep 17 00:00:00 2001 From: Derek Monturo <derek.monturo@cloudcheckr.com> Date: Sun, 16 Jul 2017 14:28:54 -0400 Subject: [PATCH 4/7] UT for #3514 unconfirmed telepath ancestral --- .../test/cards/planeswalker/JaceTest.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java index 6b2d6827bf..a0bef4a0b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java @@ -81,7 +81,7 @@ public class JaceTest extends CardTestPlayerBase { // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. - addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {2}{R} - 3/2 + addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2 addCard(Zone.HAND, playerA, "Pillarfield Ox", 1); // Flash @@ -97,7 +97,41 @@ public class JaceTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Pillarfield Ox", 1); assertExileCount("Jace, Vryn's Prodigy", 0); assertPermanentCount(playerA, "Jace, Telepath Unbound", 1); - + } + + @Test + public void vrynCannotCastAncestralVisions() { + + // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, + // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. + String jVryn = "Jace, Vryn's Prodigy"; // {U}{1} 0/2 + + //−3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. + String jTelepath = "Jace, Telepath Unbound"; // 5 loyalty + + // Sorcery, Suspend 4 {U}. Target player draws three cards. + String ancestralVision = "Ancestral Vision"; + + addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2 + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.GRAVEYARD, playerA, "Island", 4); + addCard(Zone.GRAVEYARD, playerA, ancestralVision); + addCard(Zone.HAND, playerA, "Swamp", 1); + + activateAbility(3, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard"); + setChoice(playerA, "Swamp"); + activateAbility(3, PhaseStep.BEGIN_COMBAT, playerA, "-3:"); + addTarget(playerA, ancestralVision); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, ancestralVision); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, jTelepath, 1); + assertGraveyardCount(playerA, "Swamp", 1); + assertGraveyardCount(playerA, ancestralVision, 1); + assertHandCount(playerA, 2); // 1 draw step + jace draw card + assertCounterCount(playerA, jTelepath, CounterType.LOYALTY, 5); // never changes since invalid target ? } /** From a5419d53a3ad7c0d19cc1d89fdd23f8a71c7c1c7 Mon Sep 17 00:00:00 2001 From: Derek Monturo <derek.monturo@cloudcheckr.com> Date: Sun, 16 Jul 2017 15:31:41 -0400 Subject: [PATCH 5/7] fixed UT --- .../java/org/mage/test/cards/planeswalker/JaceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java index a0bef4a0b9..1dd706e3df 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java @@ -118,9 +118,9 @@ public class JaceTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, ancestralVision); addCard(Zone.HAND, playerA, "Swamp", 1); - activateAbility(3, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard"); setChoice(playerA, "Swamp"); - activateAbility(3, PhaseStep.BEGIN_COMBAT, playerA, "-3:"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:"); addTarget(playerA, ancestralVision); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, ancestralVision); @@ -131,7 +131,7 @@ public class JaceTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Swamp", 1); assertGraveyardCount(playerA, ancestralVision, 1); assertHandCount(playerA, 2); // 1 draw step + jace draw card - assertCounterCount(playerA, jTelepath, CounterType.LOYALTY, 5); // never changes since invalid target ? + assertCounterCount(playerA, jTelepath, CounterType.LOYALTY, 2); // never changes since invalid target ? } /** From d257aafbd3926054d3c7705006f2f21e229a7cfe Mon Sep 17 00:00:00 2001 From: Derek Monturo <derek.monturo@cloudcheckr.com> Date: Sun, 16 Jul 2017 15:32:35 -0400 Subject: [PATCH 6/7] minor removal comment --- .../test/java/org/mage/test/cards/planeswalker/JaceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java index 1dd706e3df..e9d1ce042c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java @@ -131,7 +131,7 @@ public class JaceTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Swamp", 1); assertGraveyardCount(playerA, ancestralVision, 1); assertHandCount(playerA, 2); // 1 draw step + jace draw card - assertCounterCount(playerA, jTelepath, CounterType.LOYALTY, 2); // never changes since invalid target ? + assertCounterCount(playerA, jTelepath, CounterType.LOYALTY, 2); } /** From 81fb4b5d927a52eac1a44dfbcb77f864d328163a Mon Sep 17 00:00:00 2001 From: someAnonymousUser <i2369703@mvrht.net> Date: Sun, 16 Jul 2017 22:30:59 +0200 Subject: [PATCH 7/7] Curse of the Cabal (#3684) * Create CurseOfTheCabal.java * Update TimeSpiral.java --- .../src/mage/cards/c/CurseOfTheCabal.java | 187 ++++++++++++++++++ Mage.Sets/src/mage/sets/TimeSpiral.java | 1 + 2 files changed, 188 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java b/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java new file mode 100644 index 0000000000..fa1cb57be5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheCabal.java @@ -0,0 +1,187 @@ +/* + * 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.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.common.SuspendedCondition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPlayer; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author anonymous + */ +public class CurseOfTheCabal extends CardImpl { + + public CurseOfTheCabal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{9}{B}"); + this.subtype.add("Arcane"); + + + // Target player sacrifices half the permanents he or she controls, rounded down. + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect(new CurseOfTheCabalSacrificeEffect()); + // Suspend 2-{2}{B}{B} + this.addAbility(new SuspendAbility(2, new ManaCostsImpl("{2}{B}{B}"), this)); + // At the beginning of each player's upkeep, if Curse of the Cabal is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on Curse of the Cabal. + this.addAbility(new CurseOfTheCabalTriggeredAbility()); + } + + public CurseOfTheCabal(final CurseOfTheCabal card) { + super(card); + } + + @Override + public CurseOfTheCabal copy() { + return new CurseOfTheCabal(this); + } +} + +class CurseOfTheCabalSacrificeEffect extends OneShotEffect{ + + private static final FilterControlledPermanent FILTER = new FilterControlledPermanent(); // ggf filter.FilterPermanent + + public CurseOfTheCabalSacrificeEffect() { + super(Outcome.Sacrifice); + this.staticText = "Target player sacrifices half the permanents he or she controls, rounded down."; + } + + public CurseOfTheCabalSacrificeEffect(final CurseOfTheCabalSacrificeEffect effect) { + super(effect); + } + + @Override + public CurseOfTheCabalSacrificeEffect copy() { + return new CurseOfTheCabalSacrificeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if(targetPlayer != null) { + int amount = game.getBattlefield().countAll(FILTER, targetPlayer.getId(), game) / 2; + if(amount < 1) + return true; + Target target = new TargetControlledPermanent(amount, amount, FILTER, true); + if (target.canChoose(targetPlayer.getId(), game)) { + while (!target.isChosen() && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) { + targetPlayer.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + } + for (int idx = 0; idx < target.getTargets().size(); idx++) { + Permanent permanent = game.getPermanent(target.getTargets().get(idx)); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + } + return true; + } + return false; + } +} + +class CurseOfTheCabalTriggeredAbility extends ConditionalTriggeredAbility { + + public CurseOfTheCabalTriggeredAbility() { + super(new BeginningOfUpkeepTriggeredAbility( + Zone.EXILED, new CurseOfTheCabalTriggeredAbilityConditionalDelay(), + TargetController.ANY, false, true + ), + SuspendedCondition.instance, + "At the beginning of each player's upkeep, if {this} is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on {this}." + ); + // controller has to sac a permanent + // counters aren't placed + } + + public CurseOfTheCabalTriggeredAbility(final CurseOfTheCabalTriggeredAbility effect) { + super(effect); + } + + @Override + public CurseOfTheCabalTriggeredAbility copy() { + return new CurseOfTheCabalTriggeredAbility(this); + } +} + +class CurseOfTheCabalTriggeredAbilityConditionalDelay extends AddCountersSourceEffect{ + + public CurseOfTheCabalTriggeredAbilityConditionalDelay(){ + super(CounterType.TIME.createInstance(), new StaticValue(2), false, true); + } + + public boolean apply(Game game, Ability source) { + UUID id = game.getActivePlayerId(); + Player target = game.getPlayer(id); + Cost cost = new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledPermanent())); + if(target == null) + return false; + if (cost.canPay(source, source.getSourceId(), id, game) + && target.chooseUse(Outcome.Sacrifice, "Sacrifice a permanent to delay Curse of the Cabal?", source, game) + && cost.pay(source, game, source.getSourceId(), id, true, null)) { + return super.apply(game, source); + } + return true; + } + + public CurseOfTheCabalTriggeredAbilityConditionalDelay(final CurseOfTheCabalTriggeredAbilityConditionalDelay effect) { + super(effect); + } + + @Override + public CurseOfTheCabalTriggeredAbilityConditionalDelay copy() { + return new CurseOfTheCabalTriggeredAbilityConditionalDelay(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TimeSpiral.java b/Mage.Sets/src/mage/sets/TimeSpiral.java index 5317902281..05856144fa 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiral.java +++ b/Mage.Sets/src/mage/sets/TimeSpiral.java @@ -68,6 +68,7 @@ public class TimeSpiral extends ExpansionSet { cards.add(new SetCardInfo("Coral Trickster", 54, Rarity.COMMON, mage.cards.c.CoralTrickster.class)); cards.add(new SetCardInfo("Corpulent Corpse", 98, Rarity.COMMON, mage.cards.c.CorpulentCorpse.class)); cards.add(new SetCardInfo("Crookclaw Transmuter", 55, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class)); + cards.add(new SetCardInfo("Curse of the Cabal", 99, Rarity.RARE, mage.cards.c.CurseOfTheCabal.class)); cards.add(new SetCardInfo("Dark Withering", 101, Rarity.COMMON, mage.cards.d.DarkWithering.class)); cards.add(new SetCardInfo("D'Avenant Healer", 11, Rarity.COMMON, mage.cards.d.DAvenantHealer.class)); cards.add(new SetCardInfo("Deathspore Thallid", 102, Rarity.COMMON, mage.cards.d.DeathsporeThallid.class));