From eaae3cc9b03a182c00e5888a78963482046916ae Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 20 Apr 2016 13:34:32 +0200 Subject: [PATCH 01/18] * Time Spiral storage duals lands - Fixed that the activated ability to remove the counters was not defnied as mana ability. --- Mage.Sets/src/mage/sets/commander/DreadshipReef.java | 5 +++-- Mage.Sets/src/mage/sets/commander/FungalReaches.java | 5 +++-- Mage.Sets/src/mage/sets/commander2013/MoltenSlagheap.java | 5 +++-- Mage.Sets/src/mage/sets/commander2013/SaltcrustedSteppe.java | 5 +++-- Mage.Sets/src/mage/sets/oathofthegatewatch/VileRedeemer.java | 4 +++- Mage.Sets/src/mage/sets/timespiral/CalciformPools.java | 5 +++-- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander/DreadshipReef.java b/Mage.Sets/src/mage/sets/commander/DreadshipReef.java index e121b59e4e9..cf95d65c13f 100644 --- a/Mage.Sets/src/mage/sets/commander/DreadshipReef.java +++ b/Mage.Sets/src/mage/sets/commander/DreadshipReef.java @@ -37,6 +37,7 @@ import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; @@ -57,11 +58,11 @@ public class DreadshipReef extends CardImpl { // {tap}: Add {C} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Put a storage counter on Dreadship Reef. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Dreadship Reef: Add X mana in any combination of {U} and/or {B} to your mana pool. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(new RemovedCountersForCostValue(), ColoredManaSymbol.U, ColoredManaSymbol.B), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); diff --git a/Mage.Sets/src/mage/sets/commander/FungalReaches.java b/Mage.Sets/src/mage/sets/commander/FungalReaches.java index 4be3a004ec6..5932fc05f4a 100644 --- a/Mage.Sets/src/mage/sets/commander/FungalReaches.java +++ b/Mage.Sets/src/mage/sets/commander/FungalReaches.java @@ -37,6 +37,7 @@ import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; @@ -58,12 +59,12 @@ public class FungalReaches extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Put a storage counter on Fungal Reaches. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Fungal Reaches: Add X mana in any combination of {R} and/or {G} to your mana pool. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(new RemovedCountersForCostValue(), ColoredManaSymbol.R, ColoredManaSymbol.G), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); diff --git a/Mage.Sets/src/mage/sets/commander2013/MoltenSlagheap.java b/Mage.Sets/src/mage/sets/commander2013/MoltenSlagheap.java index 7b7a554e7b3..9dbfa3b39c8 100644 --- a/Mage.Sets/src/mage/sets/commander2013/MoltenSlagheap.java +++ b/Mage.Sets/src/mage/sets/commander2013/MoltenSlagheap.java @@ -37,6 +37,7 @@ import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; @@ -57,11 +58,11 @@ public class MoltenSlagheap extends CardImpl { // {tap}: Add {C} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Put a storage counter on Molten Slagheap. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Molten Slagheap: Add X mana in any combination of {B} and/or {R} to your mana pool. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(new RemovedCountersForCostValue(), ColoredManaSymbol.B, ColoredManaSymbol.R), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); diff --git a/Mage.Sets/src/mage/sets/commander2013/SaltcrustedSteppe.java b/Mage.Sets/src/mage/sets/commander2013/SaltcrustedSteppe.java index 44343b08895..8d749649633 100644 --- a/Mage.Sets/src/mage/sets/commander2013/SaltcrustedSteppe.java +++ b/Mage.Sets/src/mage/sets/commander2013/SaltcrustedSteppe.java @@ -37,6 +37,7 @@ import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; @@ -57,11 +58,11 @@ public class SaltcrustedSteppe extends CardImpl { // {tap}: Add {C} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Put a storage counter on Saltcrusted Steppe. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Saltcrusted Steppe: Add X mana in any combination of {G} and/or {W} to your mana pool. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(new RemovedCountersForCostValue(), ColoredManaSymbol.G, ColoredManaSymbol.W), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); diff --git a/Mage.Sets/src/mage/sets/oathofthegatewatch/VileRedeemer.java b/Mage.Sets/src/mage/sets/oathofthegatewatch/VileRedeemer.java index 1412d9a9691..b879db59eb3 100644 --- a/Mage.Sets/src/mage/sets/oathofthegatewatch/VileRedeemer.java +++ b/Mage.Sets/src/mage/sets/oathofthegatewatch/VileRedeemer.java @@ -137,7 +137,8 @@ class VileRedeemerNonTokenCreaturesDiedWatcher extends Watcher { if (zEvent.isDiesEvent() && zEvent.getTarget() != null && zEvent.getTarget().getCardType().contains(CardType.CREATURE) && !(zEvent.getTarget() instanceof PermanentToken)) { - int count = amountOfCreaturesThatDied.containsKey(zEvent.getTarget().getControllerId()) ? amountOfCreaturesThatDied.get(zEvent.getTarget().getControllerId()) : 0; + int count = amountOfCreaturesThatDied.containsKey(zEvent.getTarget().getControllerId()) + ? amountOfCreaturesThatDied.get(zEvent.getTarget().getControllerId()) : 0; amountOfCreaturesThatDied.put(zEvent.getTarget().getControllerId(), ++count); } } @@ -145,6 +146,7 @@ class VileRedeemerNonTokenCreaturesDiedWatcher extends Watcher { @Override public void reset() { + super.reset(); amountOfCreaturesThatDied.clear(); } diff --git a/Mage.Sets/src/mage/sets/timespiral/CalciformPools.java b/Mage.Sets/src/mage/sets/timespiral/CalciformPools.java index 12a3dc3af73..14d38a0e444 100644 --- a/Mage.Sets/src/mage/sets/timespiral/CalciformPools.java +++ b/Mage.Sets/src/mage/sets/timespiral/CalciformPools.java @@ -37,6 +37,7 @@ import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; @@ -57,11 +58,11 @@ public class CalciformPools extends CardImpl { // {tap}: Add {C} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Put a storage counter on Calciform Pools. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {1}, Remove X storage counters from Calciform Pools: Add X mana in any combination of {W} and/or {U} to your mana pool. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(new RemovedCountersForCostValue(), ColoredManaSymbol.W, ColoredManaSymbol.U), new GenericManaCost(1)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); From 9cc1c3edd584d0ba8ff95c0a562715283d9744c5 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 20 Apr 2016 15:40:15 +0200 Subject: [PATCH 02/18] * Kindly Stranger - Fixed that it did not allow destroying creature on transform (fixes #1888). --- .../DemonPossessedWitch.java | 51 ++++++++++++++++-- .../shadowsoverinnistrad/KindlyStranger.java | 52 ------------------- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/DemonPossessedWitch.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/DemonPossessedWitch.java index 841dfb8678b..a1608d4efac 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/DemonPossessedWitch.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/DemonPossessedWitch.java @@ -29,12 +29,18 @@ package mage.sets.shadowsoverinnistrad; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.InfoEffect; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.TargetPermanent; /** * @@ -57,7 +63,7 @@ public class DemonPossessedWitch extends CardImpl { this.nightCard = true; // When this creature transforms into Demon-Possessed Witch, you may destroy target creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(rule))); + this.addAbility(new DemonPossessedWitchAbility()); } public DemonPossessedWitch(final DemonPossessedWitch card) { @@ -69,3 +75,42 @@ public class DemonPossessedWitch extends CardImpl { return new DemonPossessedWitch(this); } } + +class DemonPossessedWitchAbility extends TriggeredAbilityImpl { + + public DemonPossessedWitchAbility() { + super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); + Target target = new TargetPermanent(new FilterCreaturePermanent()); + this.addTarget(target); + } + + public DemonPossessedWitchAbility(final DemonPossessedWitchAbility ability) { + super(ability); + } + + @Override + public DemonPossessedWitchAbility copy() { + return new DemonPossessedWitchAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(sourceId)) { + Permanent permanent = game.getPermanent(sourceId); + if (permanent != null && permanent.isTransformed()) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "When this creature transforms into Demon-Possessed Witch, you may destroy target creature."; + } +} diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/KindlyStranger.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/KindlyStranger.java index e96049f06b7..0327937f5df 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/KindlyStranger.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/KindlyStranger.java @@ -29,23 +29,15 @@ package mage.sets.shadowsoverinnistrad; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.condition.common.DeliriumCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; -import mage.target.TargetPermanent; /** * @@ -67,9 +59,6 @@ public class KindlyStranger extends CardImpl { this.addAbility(new TransformAbility()); this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl<>("{2}{B}"), DeliriumCondition.getInstance())); - - // When this creature transforms into Demon-Possessed Witch, you may destroy target creature. - this.addAbility(new DemonPossessedWitchAbility()); } public KindlyStranger(final KindlyStranger card) { @@ -81,44 +70,3 @@ public class KindlyStranger extends CardImpl { return new KindlyStranger(this); } } - -class DemonPossessedWitchAbility extends TriggeredAbilityImpl { - - public DemonPossessedWitchAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); - Target target = new TargetPermanent(new FilterCreaturePermanent()); - this.addTarget(target); - // Rule only shown on the night side - this.setRuleVisible(false); - } - - public DemonPossessedWitchAbility(final DemonPossessedWitchAbility ability) { - super(ability); - } - - @Override - public DemonPossessedWitchAbility copy() { - return new DemonPossessedWitchAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TRANSFORMED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(sourceId)) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null && permanent.isTransformed()) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When this creature transforms into Demon-Possessed Witch, you may destroy target creature."; - } -} From f88fb6fcaed5e9239a8cb6dc13d4b1218abb63b1 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Wed, 20 Apr 2016 09:59:58 -0500 Subject: [PATCH 03/18] Randomized which Clue tokens appear for SOI. --- .../permanent/token/ClueArtifactToken.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java b/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java index f7d2d2f796f..d1e64b27f8d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,13 +20,17 @@ * 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -36,13 +40,19 @@ import mage.constants.CardType; import mage.constants.Zone; /** - * + * */ public class ClueArtifactToken extends Token { + final static private List tokenImageSets = new ArrayList<>(); + + static { + tokenImageSets.addAll(Arrays.asList("SOI", "EDM")); + } + public ClueArtifactToken() { super("Clue", "colorless Clue artifact token onto the battlefield with \"{2}, Sacrifice this artifact: Draw a card.\""); - this.setOriginalExpansionSetCode("SOI"); + availableImageSetCodes = tokenImageSets; cardType.add(CardType.ARTIFACT); subtype.add("Clue"); @@ -53,4 +63,25 @@ public class ClueArtifactToken extends Token { ability.addCost(cost); this.addAbility(ability); } -} \ No newline at end of file + + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + if (getOriginalExpansionSetCode().equals("SOI")) { + this.setTokenType(new Random().nextInt(6) + 1); // 6 different images + } + if (getOriginalExpansionSetCode().equals("EDM")) { + this.setTokenType(new Random().nextInt(6) + 1); // 6 different images + } + } + + public ClueArtifactToken(final ClueArtifactToken token) { + super(token); + } + + @Override + public ClueArtifactToken copy() { + return new ClueArtifactToken(this); + } +} From 6ff458d86ccb1c10b6551f6f6bb11d13041729f2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 21 Apr 2016 00:19:32 +0200 Subject: [PATCH 04/18] * Reworked Dragon Whelp activation check. --- .../src/mage/sets/magic2010/DragonWhelp.java | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java index c17df470e2d..1963f833b92 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java +++ b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java @@ -35,6 +35,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -44,7 +45,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * @@ -60,7 +61,10 @@ public class DragonWhelp extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); + // Flying this.addAbility(FlyingAbility.getInstance()); + + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")); @@ -80,6 +84,17 @@ public class DragonWhelp extends CardImpl { class DragonWhelpEffect extends OneShotEffect { + class ActivationInfo { + + public int turnNum; + public int activationCounter; + + public ActivationInfo(int turnNum, int activationCounter) { + this.turnNum = turnNum; + this.activationCounter = activationCounter; + } + } + public DragonWhelpEffect() { super(Outcome.Damage); this.staticText = "If this ability has been activated four or more times this turn, sacrifice {this} at the beginning of the next end step"; @@ -96,46 +111,28 @@ class DragonWhelpEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "DragonWhelp"); - if (amount == null) { - amount = 0; - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DragonWhelpDelayedEffect()); + ActivationInfo activationInfo = getActivationInfo(source, game); + ++activationInfo.activationCounter; + setActivationInfo(activationInfo, source, game); + if (activationInfo.activationCounter == 4) { + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); game.addDelayedTriggeredAbility(delayedAbility, source); } - amount++; - game.getState().setValue(source.getSourceId().toString() + "DragonWhelp", amount); - return true; } -} -class DragonWhelpDelayedEffect extends OneShotEffect { - - public DragonWhelpDelayedEffect() { - super(Outcome.Damage); - this.staticText = "If this ability has been activated four or more times this turn, sacrifice {this} at the beginning of the next end step"; - } - - public DragonWhelpDelayedEffect(final DragonWhelpDelayedEffect effect) { - super(effect); - } - - @Override - public DragonWhelpDelayedEffect copy() { - return new DragonWhelpDelayedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "DragonWhelp"); - if (amount != null && amount >= 4) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } + private ActivationInfo getActivationInfo(Ability source, Game game) { + Integer turnNum = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsTurn", source.getSourceId(), game)); + Integer activationCount = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsCount", source.getSourceId(), game)); + if (turnNum == null || activationCount == null) { + turnNum = game.getTurnNum(); + activationCount = 0; } - game.getState().setValue(source.getSourceId().toString() + "DragonWhelp", null); + return new ActivationInfo(turnNum, activationCount); + } - return true; + private void setActivationInfo(ActivationInfo activationInfo, Ability source, Game game) { + game.getState().setValue(CardUtil.getCardZoneString("activationsTurn", source.getSourceId(), game), activationInfo.turnNum); + game.getState().setValue(CardUtil.getCardZoneString("activationsCount", source.getSourceId(), game), activationInfo.activationCounter); } } From 181381b26211932cb0c455a300fc7964ab91bc93 Mon Sep 17 00:00:00 2001 From: drmDev Date: Wed, 20 Apr 2016 23:18:21 -0400 Subject: [PATCH 05/18] Prismatic Strands impl --- .../mage/sets/judgment/PrismaticStrands.java | 121 ++++++++++++++++++ .../sets/magic2015/AvacynGuardianAngel.java | 3 - .../main/java/mage/game/events/GameEvent.java | 1 - 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java diff --git a/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java b/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java new file mode 100644 index 00000000000..17482f7c54a --- /dev/null +++ b/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java @@ -0,0 +1,121 @@ +/* + * 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.sets.judgment; + +import java.util.UUID; +import mage.MageObject; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.choices.ChoiceColor; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TimingRule; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class PrismaticStrands extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped white creature you control"); + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + public PrismaticStrands(UUID ownerId) { + super(ownerId, 18, "Prismatic Strands", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); + this.expansionSetCode = "JUD"; + + // Prevent all damage that sources of the color of your choice would deal this turn. + this.getSpellAbility().addEffect(new PrismaticStrandsEffect(Duration.EndOfTurn, Integer.MAX_VALUE, false)); + this.getSpellAbility().addChoice(new ChoiceColor()); + + // Flashback-Tap an untapped white creature you control. + this.addAbility(new FlashbackAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1,1,filter,false)), TimingRule.INSTANT)); + } + + public PrismaticStrands(final PrismaticStrands card) { + super(card); + } + + @Override + public PrismaticStrands copy() { + return new PrismaticStrands(this); + } +} + +class PrismaticStrandsEffect extends PreventionEffectImpl { + + public PrismaticStrandsEffect(Duration duration, int amount, boolean onlyCombat) { + super(duration, amount, onlyCombat, false); + this.staticText = "Prevent all damage that sources of the color of your choice would deal this turn"; + } + + public PrismaticStrandsEffect(PrismaticStrandsEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + if (event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) + || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER)) { + ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); + MageObject sourceObject = game.getObject(event.getSourceId()); + if (sourceObject != null && choice != null && sourceObject.getColor(game).shares(choice.getColor())) { + return true; + } + } + } + return false; + } + + @Override + public PrismaticStrandsEffect copy() { + return new PrismaticStrandsEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java index 4ba93b9a898..e8b4d1ce9dd 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java +++ b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java @@ -30,7 +30,6 @@ package mage.sets.magic2015; import java.util.UUID; import mage.MageInt; import mage.MageObject; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -152,8 +151,6 @@ class AvacynGuardianAngelPreventToPlayerEffect extends PreventionEffectImpl { super(effect); } - - @Override public boolean apply(Game game, Ability source) { return true; diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 9497c485d4f..8c6856acdc1 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -51,7 +51,6 @@ public class GameEvent implements Serializable { public enum EventType { //Game events -//Game events BEGINNING, PREVENT_DAMAGE, PREVENTED_DAMAGE, //Turn-based events From eb960a34ad40eb379f488f7bc4a77db0da258d7e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 21 Apr 2016 11:46:02 +0200 Subject: [PATCH 06/18] * Dragon Whelp - Made the activation handling more reusable friendly and object sensitive (zone change counter). --- .../src/mage/sets/magic2010/DragonWhelp.java | 34 +--- .../other/LimitedCountedActivationsTest.java | 158 ++++++++++++++++++ .../java/mage/abilities/ActivationInfo.java | 77 +++++++++ 3 files changed, 239 insertions(+), 30 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java create mode 100644 Mage/src/main/java/mage/abilities/ActivationInfo.java diff --git a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java index 1963f833b92..091a8fe99e5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java +++ b/Mage.Sets/src/mage/sets/magic2010/DragonWhelp.java @@ -30,6 +30,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.ActivationInfo; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -45,7 +46,6 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.util.CardUtil; /** * @@ -84,17 +84,6 @@ public class DragonWhelp extends CardImpl { class DragonWhelpEffect extends OneShotEffect { - class ActivationInfo { - - public int turnNum; - public int activationCounter; - - public ActivationInfo(int turnNum, int activationCounter) { - this.turnNum = turnNum; - this.activationCounter = activationCounter; - } - } - public DragonWhelpEffect() { super(Outcome.Damage); this.staticText = "If this ability has been activated four or more times this turn, sacrifice {this} at the beginning of the next end step"; @@ -111,28 +100,13 @@ class DragonWhelpEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ActivationInfo activationInfo = getActivationInfo(source, game); - ++activationInfo.activationCounter; - setActivationInfo(activationInfo, source, game); - if (activationInfo.activationCounter == 4) { + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + activationInfo.addActivation(game); + if (activationInfo.getActivationCounter() == 4) { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); game.addDelayedTriggeredAbility(delayedAbility, source); } return true; } - private ActivationInfo getActivationInfo(Ability source, Game game) { - Integer turnNum = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsTurn", source.getSourceId(), game)); - Integer activationCount = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsCount", source.getSourceId(), game)); - if (turnNum == null || activationCount == null) { - turnNum = game.getTurnNum(); - activationCount = 0; - } - return new ActivationInfo(turnNum, activationCount); - } - - private void setActivationInfo(ActivationInfo activationInfo, Ability source, Game game) { - game.getState().setValue(CardUtil.getCardZoneString("activationsTurn", source.getSourceId(), game), activationInfo.turnNum); - game.getState().setValue(CardUtil.getCardZoneString("activationsCount", source.getSourceId(), game), activationInfo.activationCounter); - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java new file mode 100644 index 00000000000..15fdefd210f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java @@ -0,0 +1,158 @@ +/* + * 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.abilities.other; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class LimitedCountedActivationsTest extends CardTestPlayerBase { + + /** + * Tests usage of ActivationInfo class + */ + @Test + public void testDragonWhelpActivatedThreeTimes() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Flying + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Dragon Whelp", 1); // 3/3 + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + + attack(1, playerA, "Dragon Whelp"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Dragon Whelp", 1); + + assertLife(playerA, 20); + assertLife(playerB, 15); + } + + @Test + public void testDragonWhelpActivatedFourTimes() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + // Flying + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Dragon Whelp", 1); // 3/3 + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + + attack(1, playerA, "Dragon Whelp"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Dragon Whelp", 0); + assertGraveyardCount(playerA, "Dragon Whelp", 1); + + assertLife(playerA, 20); + assertLife(playerB, 14); + } + + @Test + public void testDragonWhelpActivatedFiveTimes() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // Flying + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Dragon Whelp", 1); // 3/3 + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: "); + + attack(1, playerA, "Dragon Whelp"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Dragon Whelp", 0); + assertGraveyardCount(playerA, "Dragon Whelp", 1); + + assertLife(playerA, 20); + assertLife(playerB, 13); + } + + @Test + public void testDragonWhelpTwoObjects() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Flying + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Dragon Whelp", 1); // 3/3 + // Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost. + addCard(Zone.HAND, playerA, "Reanimate", 1); + // Target creature gains haste until end of turn. + addCard(Zone.HAND, playerA, "Unnatural Speed", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // Destroy target nonartifact, nonblack creature. It can't be regenerated. + addCard(Zone.HAND, playerB, "Terror", 1); // {1}{B} + + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R}: "); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Terror", "Dragon Whelp"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate", "Dragon Whelp"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Unnatural Speed", "Dragon Whelp"); + + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + + attack(1, playerA, "Dragon Whelp"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerA, "Unnatural Speed", 1); + assertGraveyardCount(playerA, "Reanimate", 1); + + assertGraveyardCount(playerB, "Terror", 1); + + assertPermanentCount(playerA, "Dragon Whelp", 1); + assertPowerToughness(playerA, "Dragon Whelp", 2, 3); + assertGraveyardCount(playerA, "Dragon Whelp", 0); + + assertLife(playerA, 16); + assertLife(playerB, 15); + } +} diff --git a/Mage/src/main/java/mage/abilities/ActivationInfo.java b/Mage/src/main/java/mage/abilities/ActivationInfo.java new file mode 100644 index 00000000000..84d6b1b8db0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/ActivationInfo.java @@ -0,0 +1,77 @@ +/* + * 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; + +import java.util.UUID; +import mage.game.Game; + +/** + * The ActivationInfo class holds the information how often an ability of an + * object was activated during a turn. It handles the check, if the object is + * still the same, so for example if a permanent left battlefield and returns, + * the counting of activations happens for each object. + * + * @author LevelX2 + */ +public class ActivationInfo { + + protected int turnNum; + protected int activationCounter; + + public static ActivationInfo getInstance(Game game, UUID sourceId) { + return ActivationInfo.getInstance(game, sourceId, game.getState().getZoneChangeCounter(sourceId)); + } + + public static ActivationInfo getInstance(Game game, UUID sourceId, int zoneChangeCounter) { + String key = "ActivationInfo" + sourceId.toString() + zoneChangeCounter; + ActivationInfo activationInfo = (ActivationInfo) game.getState().getValue(key); + if (activationInfo == null) { + activationInfo = new ActivationInfo(game); + game.getState().setValue(key, activationInfo); + } + return activationInfo; + } + + protected ActivationInfo(Game game) { + this.turnNum = game.getTurnNum(); + this.activationCounter = 0; + } + + public void addActivation(Game game) { + if (game.getTurnNum() != turnNum) { + activationCounter = 1; + turnNum = game.getTurnNum(); + } else { + activationCounter++; + } + } + + public int getActivationCounter() { + return activationCounter; + } +} From 0c0dd82480ba62827a54876888a195af707c2b42 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 21 Apr 2016 17:13:57 +0200 Subject: [PATCH 07/18] * Some changes to activation count and sacrifice source triggers. --- .../sets/fallenempires/FarrelitePriest.java | 53 ++++--------------- .../fallenempires/InitiatesOfTheEbonHand.java | 52 ++++-------------- .../mage/sets/journeyintonyx/BrainMaggot.java | 4 +- .../other/LimitedCountedActivationsTest.java | 47 ++++++++++++++++ .../cards/abilities/other/NecromancyTest.java | 41 +++++++------- .../cards/triggers/dies/BrainMaggotTest.java | 12 ++--- .../main/java/mage/abilities/AbilityImpl.java | 2 - .../mage/abilities/TriggeredAbilityImpl.java | 4 +- ...LeaveReturnExiledToBattlefieldAbility.java | 4 +- .../effects/common/SacrificeSourceEffect.java | 21 +++++--- 10 files changed, 116 insertions(+), 124 deletions(-) diff --git a/Mage.Sets/src/mage/sets/fallenempires/FarrelitePriest.java b/Mage.Sets/src/mage/sets/fallenempires/FarrelitePriest.java index 8378dda0a83..bbf2927b12d 100644 --- a/Mage.Sets/src/mage/sets/fallenempires/FarrelitePriest.java +++ b/Mage.Sets/src/mage/sets/fallenempires/FarrelitePriest.java @@ -31,19 +31,20 @@ import java.util.UUID; import mage.MageInt; import static mage.Mana.WhiteMana; import mage.abilities.Ability; +import mage.abilities.ActivationInfo; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.BasicManaEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -60,11 +61,11 @@ public class FarrelitePriest extends CardImpl { this.toughness = new MageInt(3); // {1}: Add {W} to your mana pool. If this ability has been activated four or more times this turn, sacrifice Farrelite Priest at the beginning of the next end step. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + SimpleManaAbility ability = new SimpleManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(WhiteMana(1)), new ManaCostsImpl("{1}")); ability.addEffect(new FarrelitePriestEffect()); - this.addAbility(ability); + this.addAbility(ability); } public FarrelitePriest(final FarrelitePriest card) { @@ -76,6 +77,7 @@ public class FarrelitePriest extends CardImpl { return new FarrelitePriest(this); } } + class FarrelitePriestEffect extends OneShotEffect { public FarrelitePriestEffect() { @@ -94,46 +96,13 @@ class FarrelitePriestEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "FarrelitePriest"); - if (amount == null) { - amount = 0; - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new FarrelitePriestResetEffect()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + activationInfo.addActivation(game); + if (activationInfo.getActivationCounter() == 4) { + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); game.addDelayedTriggeredAbility(delayedAbility, source); } - amount++; - game.getState().setValue(source.getSourceId().toString() + "FarrelitePriest", amount); - return true; } + } - -class FarrelitePriestResetEffect extends OneShotEffect { - - public FarrelitePriestResetEffect() { - super(Outcome.Neutral); - this.staticText = "If this ability has been activated four or more times this turn, sacrifice {this} at the beginning of the next end step"; - } - - public FarrelitePriestResetEffect(final FarrelitePriestResetEffect effect) { - super(effect); - } - - @Override - public FarrelitePriestResetEffect copy() { - return new FarrelitePriestResetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "FarrelitePriest"); - if (amount != null && amount >= 4) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - game.getState().setValue(source.getSourceId().toString() + "FarrelitePriest", null); - - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fallenempires/InitiatesOfTheEbonHand.java b/Mage.Sets/src/mage/sets/fallenempires/InitiatesOfTheEbonHand.java index 6ec6d8beb7d..64b3b57cb84 100644 --- a/Mage.Sets/src/mage/sets/fallenempires/InitiatesOfTheEbonHand.java +++ b/Mage.Sets/src/mage/sets/fallenempires/InitiatesOfTheEbonHand.java @@ -31,19 +31,20 @@ import java.util.UUID; import mage.MageInt; import static mage.Mana.BlackMana; import mage.abilities.Ability; +import mage.abilities.ActivationInfo; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.BasicManaEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -59,11 +60,11 @@ public class InitiatesOfTheEbonHand extends CardImpl { this.toughness = new MageInt(1); // {1}: Add {B} to your mana pool. If this ability has been activated four or more times this turn, sacrifice Initiates of the Ebon Hand at the beginning of the next end step. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + SimpleManaAbility ability = new SimpleManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(BlackMana(1)), new ManaCostsImpl("{1}")); ability.addEffect(new InitiatesOfTheEbonHandEffect()); - this.addAbility(ability); + this.addAbility(ability); } public InitiatesOfTheEbonHand(final InitiatesOfTheEbonHand card) { @@ -94,46 +95,13 @@ class InitiatesOfTheEbonHandEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "InitiatesOfTheEbonHand"); - if (amount == null) { - amount = 0; - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new InitiatesOfTheEbonHandResetEffect()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + activationInfo.addActivation(game); + if (activationInfo.getActivationCounter() == 4) { + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); game.addDelayedTriggeredAbility(delayedAbility, source); } - amount++; - game.getState().setValue(source.getSourceId().toString() + "InitiatesOfTheEbonHand", amount); - return true; } + } - -class InitiatesOfTheEbonHandResetEffect extends OneShotEffect { - - public InitiatesOfTheEbonHandResetEffect() { - super(Outcome.Neutral); - this.staticText = "If this ability has been activated four or more times this turn, sacrifice {this} at the beginning of the next end step"; - } - - public InitiatesOfTheEbonHandResetEffect(final InitiatesOfTheEbonHandResetEffect effect) { - super(effect); - } - - @Override - public InitiatesOfTheEbonHandResetEffect copy() { - return new InitiatesOfTheEbonHandResetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer amount = (Integer) game.getState().getValue(source.getSourceId().toString() + "InitiatesOfTheEbonHand"); - if (amount != null && amount >= 4) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); - } - } - game.getState().setValue(source.getSourceId().toString() + "InitiatesOfTheEbonHand", null); - - return true; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java b/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java index dd4a185cce2..ee3fb7215d3 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/BrainMaggot.java @@ -49,7 +49,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; @@ -188,8 +187,7 @@ class BrainMaggotReturnExiledCardEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1; - ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (exile != null && sourcePermanent != null) { controller.moveCards(exile, Zone.HAND, source, game); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java index 15fdefd210f..250ba777cfa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/LimitedCountedActivationsTest.java @@ -155,4 +155,51 @@ public class LimitedCountedActivationsTest extends CardTestPlayerBase { assertLife(playerA, 16); assertLife(playerB, 15); } + + @Test + public void testDragonWhelpDontSacrificeNewObject() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // Flying + // {R}: Dragon Whelp gets +1/+0 until end of turn. If this ability has been activated four or more times this turn, sacrifice Dragon Whelp at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Dragon Whelp", 1); // 3/3 + // Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost. + addCard(Zone.HAND, playerA, "Reanimate", 1); + // Target creature gains haste until end of turn. + addCard(Zone.HAND, playerA, "Unnatural Speed", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // Destroy target nonartifact, nonblack creature. It can't be regenerated. + addCard(Zone.HAND, playerB, "Terror", 1); // {1}{B} + + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R}: "); + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R}: "); + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R}: "); + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R}: "); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Terror", "Dragon Whelp"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate", "Dragon Whelp"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Unnatural Speed", "Dragon Whelp"); + + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{R}: "); + + attack(1, playerA, "Dragon Whelp"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerA, "Unnatural Speed", 1); + assertGraveyardCount(playerA, "Reanimate", 1); + + assertGraveyardCount(playerB, "Terror", 1); + + assertPermanentCount(playerA, "Dragon Whelp", 1); + assertPowerToughness(playerA, "Dragon Whelp", 2, 3); + assertGraveyardCount(playerA, "Dragon Whelp", 0); + + assertLife(playerA, 16); + assertLife(playerB, 15); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java index 5b2a9ae2f6c..4cf62035433 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -39,20 +38,18 @@ import org.mage.test.serverside.base.CardTestPlayerBase; */ public class NecromancyTest extends CardTestPlayerBase { - /** - * Necromancy - * Enchantment, 2B (3) - * You may cast Necromancy as though it had flash. If you cast it any time a - * sorcery couldn't have been cast, the controller of the permanent it - * becomes sacrifices it at the beginning of the next cleanup step. - * When Necromancy enters the battlefield, if it's on the battlefield, it - * becomes an Aura with "enchant creature put onto the battlefield with - * Necromancy." Put target creature card from a graveyard onto the - * battlefield under your control and attach Necromancy to it. When - * Necromancy leaves the battlefield, that creature's controller sacrifices it. + /** + * Necromancy Enchantment, 2B (3) You may cast Necromancy as though it had + * flash. If you cast it any time a sorcery couldn't have been cast, the + * controller of the permanent it becomes sacrifices it at the beginning of + * the next cleanup step. When Necromancy enters the battlefield, if it's on + * the battlefield, it becomes an Aura with "enchant creature put onto the + * battlefield with Necromancy." Put target creature card from a graveyard + * onto the battlefield under your control and attach Necromancy to it. When + * Necromancy leaves the battlefield, that creature's controller sacrifices + * it. * */ - @Test public void testNecromancy() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); @@ -60,7 +57,7 @@ public class NecromancyTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -77,7 +74,7 @@ public class NecromancyTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); castSpell(1, PhaseStep.UPKEEP, playerA, "Necromancy"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -87,14 +84,20 @@ public class NecromancyTest extends CardTestPlayerBase { } + /** + * Check if Necromancy is sacrificed if cast as instant and if the + * reanimated creature will be sacrificed. + */ @Test public void testNecromancyFlashSacrifice() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + // If you cast it any time a sorcery couldn't have been cast, the controller of the permanent it becomes sacrifices it at the beginning of the next cleanup step. + // When Necromancy leaves the battlefield, that creature's controller sacrifices it. addCard(Zone.HAND, playerA, "Necromancy"); - addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); + addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); // 6/4 castSpell(1, PhaseStep.UPKEEP, playerA, "Necromancy"); - + setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); @@ -114,7 +117,7 @@ public class NecromancyTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); // enchanting the Craw Wurm castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); // if Necromancy leaves, the enchanted creature has to leave too - + setStopAt(1, PhaseStep.END_TURN); execute(); @@ -124,5 +127,5 @@ public class NecromancyTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Necromancy", 1); assertGraveyardCount(playerA, "Craw Wurm", 1); } - + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java index 5bfd5cab697..49b840a07bb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/BrainMaggotTest.java @@ -36,13 +36,13 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class BrainMaggotTest extends CardTestPlayerBase { /** - * When Brain Maggot enters the battlefield, target opponent reveals his or her hand and - * you choose a nonland card from it. Exile that card until Brain Maggot leaves the battlefield. - * + * When Brain Maggot enters the battlefield, target opponent reveals his or + * her hand and you choose a nonland card from it. Exile that card until + * Brain Maggot leaves the battlefield. + * */ @Test public void testCardFromHandWillBeExiled() { @@ -61,10 +61,10 @@ public class BrainMaggotTest extends CardTestPlayerBase { assertExileCount("Bloodflow Connoisseur", 1); } - @Test public void testCardFromHandWillBeExiledAndReturn() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // When Brain Maggot enters the battlefield, target opponent reveals his or her hand and you choose a nonland card from it. Exile that card until Brain Maggot leaves the battlefield. addCard(Zone.HAND, playerA, "Brain Maggot", 2); addCard(Zone.HAND, playerB, "Bloodflow Connoisseur", 1); @@ -105,4 +105,4 @@ public class BrainMaggotTest extends CardTestPlayerBase { assertHandCount(playerB, "Bloodflow Connoisseur", 1); assertExileCount("Bloodflow Connoisseur", 0); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index cb4de5e1ecf..497fc66ddbb 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1191,8 +1191,6 @@ public abstract class AbilityImpl implements Ability { public void setSourceObject(MageObject sourceObject, Game game) { if (sourceObject == null) { this.sourceObject = game.getObject(sourceId); - // if permanent get card /permanent instead of spell - } else { this.sourceObject = sourceObject; } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 016369ac856..54e6926dcc6 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -70,7 +70,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge public void trigger(Game game, UUID controllerId) { //20091005 - 603.4 if (checkInterveningIfClause(game)) { - setSourceObject(null, game); + if (!(this instanceof DelayedTriggeredAbility)) { + setSourceObject(null, game); + } game.addTriggeredAbility(this); } } diff --git a/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledToBattlefieldAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledToBattlefieldAbility.java index 3da2208a19c..996b05d9c67 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledToBattlefieldAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledToBattlefieldAbility.java @@ -40,7 +40,6 @@ import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.util.CardUtil; @@ -107,8 +106,7 @@ class ReturnExiledPermanentsEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1; - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileZone != null) { ExileZone exile = game.getExile().getExileZone(exileZone); if (exile != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java index bb1f89ea2cf..2dfc09db98e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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; @@ -34,6 +33,7 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; /** * @@ -58,6 +58,15 @@ public class SacrificeSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject == null) { + // Check if the effect was installed by the spell the source was cast by (e.g. Necromancy), if not don't sacrifice the permanent + if (source.getSourceObject(game) instanceof Spell) { + sourceObject = game.getPermanent(source.getSourceId()); + if (sourceObject.getZoneChangeCounter(game) > source.getSourceObjectZoneChangeCounter() + 1) { + return false; + } + } + } if (sourceObject instanceof Permanent) { Permanent permanent = (Permanent) sourceObject; // you can only sacrifice a permanent you control From 410c8d9e6e49153102563f6a0cd07972e2ef5c60 Mon Sep 17 00:00:00 2001 From: drmDev Date: Thu, 21 Apr 2016 14:45:07 -0400 Subject: [PATCH 08/18] VolrathTheFallen impl --- .../sets/alarareborn/SpellboundDragon.java | 3 - .../mage/sets/nemesis/VolrathTheFallen.java | 81 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/nemesis/VolrathTheFallen.java diff --git a/Mage.Sets/src/mage/sets/alarareborn/SpellboundDragon.java b/Mage.Sets/src/mage/sets/alarareborn/SpellboundDragon.java index 45787aabb04..ad19ed8600b 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/SpellboundDragon.java +++ b/Mage.Sets/src/mage/sets/alarareborn/SpellboundDragon.java @@ -55,9 +55,6 @@ public class SpellboundDragon extends CardImpl { super(ownerId, 90, "Spellbound Dragon", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); this.expansionSetCode = "ARB"; this.subtype.add("Dragon"); - - - this.power = new MageInt(3); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/sets/nemesis/VolrathTheFallen.java b/Mage.Sets/src/mage/sets/nemesis/VolrathTheFallen.java new file mode 100644 index 00000000000..d4db92424a0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/nemesis/VolrathTheFallen.java @@ -0,0 +1,81 @@ +/* + * 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.sets.nemesis; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.DiscardCostCardConvertedMana; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class VolrathTheFallen extends CardImpl { + + public VolrathTheFallen(UUID ownerId) { + super(ownerId, 75, "Volrath the Fallen", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}{B}{B}"); + this.expansionSetCode = "NMS"; + this.supertype.add("Legendary"); + this.subtype.add("Shapeshifter"); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + + // {1}{B}, Discard a creature card: + // Volrath the Fallen gets +X/+X until end of turn, where X is the discarded card's converted mana cost. + Effect effect = new BoostSourceEffect(new DiscardCostCardConvertedMana(),new DiscardCostCardConvertedMana(),Duration.EndOfTurn); + effect.setText("{this} gets +X/+X until end of turn, where X is the discarded card's converted mana cost"); + + Ability ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + effect, + new ManaCostsImpl("{1}{B}")); + ability.addCost(new DiscardCardCost(new FilterCreatureCard())); + this.addAbility(ability); + } + + public VolrathTheFallen(final VolrathTheFallen card) { + super(card); + } + + @Override + public VolrathTheFallen copy() { + return new VolrathTheFallen(this); + } +} \ No newline at end of file From 01ee8e5a4f632cff903316889a018e25c2ecdf8e Mon Sep 17 00:00:00 2001 From: Styxo Date: Thu, 21 Apr 2016 22:18:51 +0200 Subject: [PATCH 09/18] Added Ego Erasure, Shields of Velis Vel, Springjack Knight, Turtleshell Changeling and Wellgabber Apothecary --- .../src/mage/sets/lorwyn/EgoErasure.java | 162 ++++++++++++++++++ .../src/mage/sets/lorwyn/Ringskipper.java | 42 +---- .../mage/sets/lorwyn/ShieldsOfVelisVel.java | 160 +++++++++++++++++ .../mage/sets/lorwyn/SpringjackKnight.java | 72 ++++++++ .../sets/lorwyn/TurtleshellChangeling.java | 71 ++++++++ .../sets/lorwyn/WellgabberApothecary.java | 83 +++++++++ 6 files changed, 551 insertions(+), 39 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/lorwyn/EgoErasure.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/ShieldsOfVelisVel.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/SpringjackKnight.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/TurtleshellChangeling.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/WellgabberApothecary.java diff --git a/Mage.Sets/src/mage/sets/lorwyn/EgoErasure.java b/Mage.Sets/src/mage/sets/lorwyn/EgoErasure.java new file mode 100644 index 00000000000..3eaebdac2a4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/EgoErasure.java @@ -0,0 +1,162 @@ +/* + * 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.sets.lorwyn; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.ChangelingAbility; +import mage.cards.CardImpl; +import mage.cards.repository.CardRepository; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPlayer; + +/** + * + * @author Styxo + */ +public class EgoErasure extends CardImpl { + + public EgoErasure(UUID ownerId) { + super(ownerId, 59, "Ego Erasure", Rarity.UNCOMMON, new CardType[]{CardType.TRIBAL, CardType.INSTANT}, "{2}{U}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Shapeshifter"); + + // Changeling + this.addAbility(ChangelingAbility.getInstance()); + + //Creatures target player controls get -2/+0 and lose all creature types until end of turn. + this.getSpellAbility().addEffect(new EgoErasureBoostEffect()); + this.getSpellAbility().addEffect(new EgoErasureLoseEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + public EgoErasure(final EgoErasure card) { + super(card); + } + + @Override + public EgoErasure copy() { + return new EgoErasure(this); + } +} + +class EgoErasureLoseEffect extends ContinuousEffectImpl { + + public EgoErasureLoseEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "and lose all creature types until end of turn"; + } + + public EgoErasureLoseEffect(final EgoErasureLoseEffect effect) { + super(effect); + } + + @Override + public EgoErasureLoseEffect copy() { + return new EgoErasureLoseEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (this.affectedObjectsSet) { + List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), source.getFirstTarget(), game); + for (Permanent creature : creatures) { + affectedObjectList.add(new MageObjectReference(creature, game)); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + Permanent permanent = it.next().getPermanent(game); + if (permanent != null) { + permanent.getSubtype().retainAll(CardRepository.instance.getLandTypes()); + } else { + it.remove(); + } + } + return true; + } +} + +class EgoErasureBoostEffect extends ContinuousEffectImpl { + + public EgoErasureBoostEffect() { + super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.Benefit); + staticText = "Creatures target player controls get -2/+0"; + } + + public EgoErasureBoostEffect(final EgoErasureBoostEffect effect) { + super(effect); + } + + @Override + public EgoErasureBoostEffect copy() { + return new EgoErasureBoostEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (this.affectedObjectsSet) { + List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), source.getFirstTarget(), game); + for (Permanent creature : creatures) { + affectedObjectList.add(new MageObjectReference(creature, game)); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + Permanent permanent = it.next().getPermanent(game); + if (permanent != null) { + permanent.addPower(-2); + } else { + it.remove(); + } + } + return true; + } +} + diff --git a/Mage.Sets/src/mage/sets/lorwyn/Ringskipper.java b/Mage.Sets/src/mage/sets/lorwyn/Ringskipper.java index 79443ff2a0d..395a072ad77 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/Ringskipper.java +++ b/Mage.Sets/src/mage/sets/lorwyn/Ringskipper.java @@ -29,18 +29,13 @@ package mage.sets.lorwyn; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ClashEffect; +import mage.abilities.effects.common.DoIfClashWonEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.players.Player; /** * @@ -60,8 +55,8 @@ public class Ringskipper extends CardImpl { //Flying this.addAbility(FlyingAbility.getInstance()); - //When Ringskipper is put into graveyard from play, clash with an opponent. If you win return Ringskipper to its owner's hand - this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new RingskipperEffect())); + //When {this} is put into graveyard from play, clash with an opponent. If you win return {this} to its owner's hand + this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new DoIfClashWonEffect(new ReturnToHandSourceEffect()))); } public Ringskipper(final Ringskipper card) { @@ -73,34 +68,3 @@ public class Ringskipper extends CardImpl { return new Ringskipper(this); } } - -class RingskipperEffect extends OneShotEffect { - - public RingskipperEffect() { - super(Outcome.ReturnToHand); - this.staticText = "clash with an opponent. If you win return Ringskipper to its owner's hand"; - } - - public RingskipperEffect(final RingskipperEffect effect) { - super(effect); - } - - @Override - public RingskipperEffect copy() { - return new RingskipperEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (ClashEffect.getInstance().apply(game, source)) { - ReturnToHandSourceEffect effect = new ReturnToHandSourceEffect(); - effect.apply(game, source); - } - - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/lorwyn/ShieldsOfVelisVel.java b/Mage.Sets/src/mage/sets/lorwyn/ShieldsOfVelisVel.java new file mode 100644 index 00000000000..6e0aba7ecd3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/ShieldsOfVelisVel.java @@ -0,0 +1,160 @@ +/* + * 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.sets.lorwyn; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.ChangelingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPlayer; + +/** + * + * @author Styxo + */ +public class ShieldsOfVelisVel extends CardImpl { + + public ShieldsOfVelisVel(UUID ownerId) { + super(ownerId, 39, "Shields of Velis Vel", Rarity.COMMON, new CardType[]{CardType.TRIBAL, CardType.INSTANT}, "{W}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Shapeshifter"); + + // Changeling + this.addAbility(ChangelingAbility.getInstance()); + + //Creatures target player controls get +0/+1 and gain all creature types until end of turn. + this.getSpellAbility().addEffect(new ShieldsOfVelisVelBoostEffect()); + this.getSpellAbility().addEffect(new ShieldsOfVelisVelGainEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + + } + + public ShieldsOfVelisVel(final ShieldsOfVelisVel card) { + super(card); + } + + @Override + public ShieldsOfVelisVel copy() { + return new ShieldsOfVelisVel(this); + } +} + +class ShieldsOfVelisVelGainEffect extends ContinuousEffectImpl { + + public ShieldsOfVelisVelGainEffect() { + super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "and gain all creature types until end of turn"; + } + + public ShieldsOfVelisVelGainEffect(final ShieldsOfVelisVelGainEffect effect) { + super(effect); + } + + @Override + public ShieldsOfVelisVelGainEffect copy() { + return new ShieldsOfVelisVelGainEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (this.affectedObjectsSet) { + List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), source.getFirstTarget(), game); + for (Permanent creature : creatures) { + affectedObjectList.add(new MageObjectReference(creature, game)); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + Permanent permanent = it.next().getPermanent(game); + if (permanent != null) { + permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game, false); + } else { + it.remove(); + } + } + return true; + } +} + +class ShieldsOfVelisVelBoostEffect extends ContinuousEffectImpl { + + public ShieldsOfVelisVelBoostEffect() { + super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + staticText = "Creatures target player controls get +0/+1"; + } + + public ShieldsOfVelisVelBoostEffect(final ShieldsOfVelisVelBoostEffect effect) { + super(effect); + } + + @Override + public ShieldsOfVelisVelBoostEffect copy() { + return new ShieldsOfVelisVelBoostEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (this.affectedObjectsSet) { + List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), source.getFirstTarget(), game); + for (Permanent creature : creatures) { + affectedObjectList.add(new MageObjectReference(creature, game)); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { + Permanent permanent = it.next().getPermanent(game); + if (permanent != null) { + permanent.addToughness(1); + } else { + it.remove(); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/SpringjackKnight.java b/Mage.Sets/src/mage/sets/lorwyn/SpringjackKnight.java new file mode 100644 index 00000000000..0da1aeaa469 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/SpringjackKnight.java @@ -0,0 +1,72 @@ +/* + * 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.sets.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DoIfClashWonEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Styxo + */ +public class SpringjackKnight extends CardImpl { + + public SpringjackKnight(UUID ownerId) { + super(ownerId, 41, "Springjack Knight", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Kithkin"); + this.subtype.add("Knight"); + + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + //Whenever {this} attacks, clash with an opponent. If you win, target creature gains double strike until end of turn. + Ability ability = new AttacksTriggeredAbility(new DoIfClashWonEffect(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)), false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public SpringjackKnight(final SpringjackKnight card) { + super(card); + } + + @Override + public SpringjackKnight copy() { + return new SpringjackKnight(this); + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/TurtleshellChangeling.java b/Mage.Sets/src/mage/sets/lorwyn/TurtleshellChangeling.java new file mode 100644 index 00000000000..966c07297cb --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/TurtleshellChangeling.java @@ -0,0 +1,71 @@ +/* + * 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.sets.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.SwitchPowerToughnessSourceEffect; +import mage.abilities.keyword.ChangelingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Styxo + */ +public class TurtleshellChangeling extends CardImpl { + + public TurtleshellChangeling(UUID ownerId) { + super(ownerId, 94, "Turtleshell Changeling", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Shapesifter"); + + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + //Changeling + this.addAbility(ChangelingAbility.getInstance()); + + //{1}{U}: Switch {this}'s power and toughness until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SwitchPowerToughnessSourceEffect(Duration.EndOfTurn), new ManaCostsImpl("{1}{U}"))); + } + + public TurtleshellChangeling(final TurtleshellChangeling card) { + super(card); + } + + @Override + public TurtleshellChangeling copy() { + return new TurtleshellChangeling(this); + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/WellgabberApothecary.java b/Mage.Sets/src/mage/sets/lorwyn/WellgabberApothecary.java new file mode 100644 index 00000000000..6e99d35e3ea --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/WellgabberApothecary.java @@ -0,0 +1,83 @@ +/* + * 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.sets.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventDamageToTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Styxo + */ +public class WellgabberApothecary extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("target tapped Merfolk or Kithkin creature this turn"); + + static { + filter.add(new TappedPredicate()); + filter.add(Predicates.or(new SubtypePredicate("Merfolk"), new SubtypePredicate("Kithkin"))); + } + + public WellgabberApothecary(UUID ownerId) { + super(ownerId, 47, "Wellgabber Apothecary", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{4}{W}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Merfolk"); + this.subtype.add("Cleric"); + + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {1}{W} : Prevent all damage that would be dealt to target tapped Merfolk or Kithkin creatuer this turn + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE), new ManaCostsImpl("{1}{W}")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + + } + + public WellgabberApothecary(final WellgabberApothecary card) { + super(card); + } + + @Override + public WellgabberApothecary copy() { + return new WellgabberApothecary(this); + } +} From 109506ad63cd9cc5d192c4c95bb89a9fc1c54c5f Mon Sep 17 00:00:00 2001 From: drmDev Date: Thu, 21 Apr 2016 17:35:11 -0400 Subject: [PATCH 10/18] Calming Verse impl --- .../src/mage/sets/prophecy/CalmingVerse.java | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/prophecy/CalmingVerse.java diff --git a/Mage.Sets/src/mage/sets/prophecy/CalmingVerse.java b/Mage.Sets/src/mage/sets/prophecy/CalmingVerse.java new file mode 100644 index 00000000000..292127cd068 --- /dev/null +++ b/Mage.Sets/src/mage/sets/prophecy/CalmingVerse.java @@ -0,0 +1,124 @@ +/* + * 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.sets.prophecy; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class CalmingVerse extends CardImpl { + + public CalmingVerse(UUID ownerId) { + super(ownerId, 110, "Calming Verse", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{3}{G}"); + this.expansionSetCode = "PCY"; + + // Destroy all enchantments you don't control. Then, if you control an untapped land, destroy all enchantments you control. + this.getSpellAbility().addEffect(new CalmingVerseEffect()); + + } + + public CalmingVerse(final CalmingVerse card) { + super(card); + } + + @Override + public CalmingVerse copy() { + return new CalmingVerse(this); + } +} + +class CalmingVerseEffect extends OneShotEffect { + + private static final FilterPermanent untappedLandFilter = new FilterPermanent("If you control an untapped land"); + static { + untappedLandFilter.add(new CardTypePredicate(CardType.LAND)); + untappedLandFilter.add(Predicates.not(new TappedPredicate())); + } + + private static final FilterEnchantmentPermanent opponentEnchantmentsFilter = new FilterEnchantmentPermanent("enchantments you don't control"); + static { + opponentEnchantmentsFilter.add(new ControllerPredicate(TargetController.OPPONENT)); + } + + private static final FilterControlledEnchantmentPermanent controlledEnchantmentsFilter = new FilterControlledEnchantmentPermanent("enchantments you control"); + + + public CalmingVerseEffect() { + super(Outcome.Detriment); + this.staticText = "Destroy all enchantments you don't control. Then, if you control an untapped land, destroy all enchantments you control"; + } + + public CalmingVerseEffect(final CalmingVerseEffect effect) { + super(effect); + } + + @Override + public CalmingVerseEffect copy() { + return new CalmingVerseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // Destroy all other enchantments + for (Permanent permanent : game.getBattlefield().getActivePermanents(opponentEnchantmentsFilter, source.getControllerId(), source.getSourceId(), game)) { + permanent.destroy(source.getSourceId(), game, false); + } + + // Then if you control an untapped land, destroy all own enchantments + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + + if (game.getState().getBattlefield().countAll(untappedLandFilter, controller.getId(), game) > 0) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(controlledEnchantmentsFilter, source.getControllerId(), source.getSourceId(), game)) { + permanent.destroy(source.getSourceId(), game, false); + } + } + + } + return true; + } +} From 8c912851ddec49d7254b3490f3e5786ffd146099 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 22 Apr 2016 08:32:09 +1000 Subject: [PATCH 11/18] spjspj - Update SourceHasRemainedInSameZoneCondition with new constructor that passes in game --- .../common/SourceHasRemainedInSameZoneCondition.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceHasRemainedInSameZoneCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHasRemainedInSameZoneCondition.java index 53629a71d03..3b77c2b6490 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceHasRemainedInSameZoneCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHasRemainedInSameZoneCondition.java @@ -48,6 +48,14 @@ public class SourceHasRemainedInSameZoneCondition implements Condition { this.idToCheck = idToCheck; this.timesChangedZones = -1; } + + public SourceHasRemainedInSameZoneCondition(UUID idToCheck, Game game) { + this.idToCheck = idToCheck; + this.timesChangedZones = -1; + if (this.idToCheck != null && game != null && game.getCard(this.idToCheck) != null) { + this.timesChangedZones = game.getState().getZoneChangeCounter(this.idToCheck); + } + } public SourceHasRemainedInSameZoneCondition getInstance(UUID cardId) { return new SourceHasRemainedInSameZoneCondition(cardId); From e9ad2fb6aff3110367463e553e28f7d08c9a76c9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 22 Apr 2016 08:34:39 +1000 Subject: [PATCH 12/18] spjspj - Missed DRK printing of Preacher --- Mage.Sets/src/mage/sets/thedark/Preacher.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/thedark/Preacher.java diff --git a/Mage.Sets/src/mage/sets/thedark/Preacher.java b/Mage.Sets/src/mage/sets/thedark/Preacher.java new file mode 100644 index 00000000000..c74472babeb --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Preacher.java @@ -0,0 +1,52 @@ +/* + * 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.sets.thedark; + +import java.util.UUID; + +/** + * + * @author spjspj + */ +public class Preacher extends mage.sets.mastersedition.Preacher { + + public Preacher(UUID ownerId) { + super(ownerId); + this.cardNumber = 89; + this.expansionSetCode = "DRK"; + } + + public Preacher(final Preacher card) { + super(card); + } + + @Override + public Preacher copy() { + return new Preacher(this); + } +} From d6d8042a124d6b05091886383a531094a541daab Mon Sep 17 00:00:00 2001 From: drmDev Date: Thu, 21 Apr 2016 23:40:30 -0400 Subject: [PATCH 13/18] WildDogs impl --- .../src/mage/sets/urzassaga/WildDogs.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/urzassaga/WildDogs.java diff --git a/Mage.Sets/src/mage/sets/urzassaga/WildDogs.java b/Mage.Sets/src/mage/sets/urzassaga/WildDogs.java new file mode 100644 index 00000000000..df838002859 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzassaga/WildDogs.java @@ -0,0 +1,128 @@ +/* + * 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.sets.urzassaga; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class WildDogs extends CardImpl { + + public WildDogs(UUID ownerId) { + super(ownerId, 284, "Wild Dogs", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{G}"); + this.expansionSetCode = "USG"; + this.subtype.add("Hound"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // At the beginning of your upkeep, if a player has more life than each other player, the player with the most life gains control of Wild Dogs. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WildDogsEffect(), TargetController.YOU, false)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + public WildDogs(final WildDogs card) { + super(card); + } + + @Override + public WildDogs copy() { + return new WildDogs(this); + } +} + +class WildDogsEffect extends OneShotEffect { + + public WildDogsEffect() { + super(Outcome.GainControl); + this.staticText = "the player with the most life gains control of {this}"; + } + + public WildDogsEffect(final WildDogsEffect effect) { + super(effect); + } + + @Override + public WildDogsEffect copy() { + return new WildDogsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null) { + Player newController = null; + int lowLife = Integer.MIN_VALUE; + boolean tie = false; + for (UUID playerID : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerID); + if (player != null) { + if (player.getLife() > lowLife) { + lowLife = player.getLife(); + newController = player; + tie = false; + } else if (player.getLife() == lowLife) { + tie = true; + } + } + } + if (!controller.equals(newController) && !tie && newController != null) { + ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); + effect.setTargetPointer(new FixedTarget(sourcePermanent, game)); + game.addEffect(effect, source); + } + } + return true; + } + return false; + + } +} \ No newline at end of file From 412c39549402842e2861a8a504beb254381e2a59 Mon Sep 17 00:00:00 2001 From: Kenny Wottrich Date: Sat, 23 Apr 2016 19:31:36 -0500 Subject: [PATCH 14/18] Fix Effect Logic Both players must not be null in order for the hand size check to succeed --- Mage.Sets/src/mage/sets/eighthedition/BalanceOfPower.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/eighthedition/BalanceOfPower.java b/Mage.Sets/src/mage/sets/eighthedition/BalanceOfPower.java index 27d0af1f23d..fa6ec27ac20 100644 --- a/Mage.Sets/src/mage/sets/eighthedition/BalanceOfPower.java +++ b/Mage.Sets/src/mage/sets/eighthedition/BalanceOfPower.java @@ -85,7 +85,7 @@ class BalanceOfPowerEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(source.getFirstTarget()); - if (opponent != null || player != null && opponent.getHand().size() > player.getHand().size()) { + if (opponent != null && player != null && opponent.getHand().size() > player.getHand().size()) { player.drawCards(opponent.getHand().size() - player.getHand().size(), game); return true; } From 48035a053e9e73357ad5e26fe21f7b49a2c0a413 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 24 Apr 2016 09:38:02 +0200 Subject: [PATCH 15/18] * Myr Superion - Fixed that its mana costs could not be decreased by convoke (fixes #1897). --- Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java index 99ee8fe07ef..4eb8127c2e0 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java @@ -35,7 +35,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterCreatureCard; /** * @@ -43,7 +43,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public class MyrSuperion extends CardImpl { - private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static FilterCreatureCard filter = new FilterCreatureCard(); public MyrSuperion(UUID ownerId) { super(ownerId, 146, "Myr Superion", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); From 44100968b70a4788146cd21ee23d8d27836410b7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 24 Apr 2016 09:38:39 +0200 Subject: [PATCH 16/18] Fixed internal wrong spelling. --- Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java b/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java index 43208086977..a3a4fc224bf 100644 --- a/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java +++ b/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java @@ -124,7 +124,7 @@ class ActivatedLoyaltyAbilityWatcher extends Watcher { return new ActivatedLoyaltyAbilityWatcher(this); } - public boolean activatedLayaltyAbility(UUID playerId) { + public boolean activatedLoyaltyAbility(UUID playerId) { return playerIds.contains(playerId); } } @@ -177,7 +177,7 @@ class TheChainVeilCondition implements Condition { public boolean apply(Game game, Ability source) { ActivatedLoyaltyAbilityWatcher watcher = (ActivatedLoyaltyAbilityWatcher) game.getState().getWatchers().get("ActivatedLoyaltyAbilityWatcher"); if (watcher != null) { - if (!watcher.activatedLayaltyAbility(source.getControllerId())) { + if (!watcher.activatedLoyaltyAbility(source.getControllerId())) { return true; } } From 3cc6d97fbdbe0981b1422440251d85bc08abadbb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 24 Apr 2016 09:38:54 +0200 Subject: [PATCH 17/18] Added test. --- .../cards/asthough/SpendOtherManaTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java index a81a40c044f..d649b5b5aec 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/SpendOtherManaTest.java @@ -97,4 +97,41 @@ public class SpendOtherManaTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Chandra, Flamecaller", 1); } + + /** + * I was unable to cast Nissa, Voice of Zendikar using black mana with Oath + * of Nissa in play. Pretty sure Oath is working usually, so here were the + * conditions in my game: + * + * -Cast Dark Petition with spell mastery -Attempt to cast Nissa, Voice of + * Zendikar using the triple black mana from Dark Petition + */ + @Test + public void testOathOfNissaWithDarkPetition() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // When Oath of Nissa enters the battlefield, look at the top three cards of your library. You may reveal a creature, land, or planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in any order. + // You may spend mana as though it were mana of any color to cast planeswalker spells. + addCard(Zone.BATTLEFIELD, playerA, "Oath of Nissa"); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 2); + + // Search your library for a card and put that card into your hand. Then shuffle your library. + // Spell mastery - If there are two or more instant and/or sorcery cards in your graveyard, add {B}{B}{B} to your mana pool. + addCard(Zone.HAND, playerA, "Dark Petition"); // {3}{B}{B} + + // +1: Put a 0/1 green Plant creature token onto the battlefield. + // -2: Put a +1/+1 counter on each creature you control. + // -7: You gain X life and draw X cards, where X is the number of lands you control. + addCard(Zone.LIBRARY, playerA, "Nissa, Voice of Zendikar"); // {1}{G}{G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dark Petition"); + setChoice(playerA, "Nissa, Voice of Zendikar"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nissa, Voice of Zendikar"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Dark Petition", 1); + assertHandCount(playerA, "Nissa, Voice of Zendikar", 0); + assertPermanentCount(playerA, "Nissa, Voice of Zendikar", 1); + } } From d626b072cd2048be0c79a4f4a4cde3d225cbb60d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 24 Apr 2016 12:34:35 +0200 Subject: [PATCH 18/18] * Myr Superion - Fixed that its mana costs could not be decreased by convoke. --- Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java | 4 ++-- Mage/src/main/java/mage/players/ManaPool.java | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java index 4eb8127c2e0..99ee8fe07ef 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MyrSuperion.java @@ -35,7 +35,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; /** * @@ -43,7 +43,7 @@ import mage.filter.common.FilterCreatureCard; */ public class MyrSuperion extends CardImpl { - private static FilterCreatureCard filter = new FilterCreatureCard(); + private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); public MyrSuperion(UUID ownerId) { super(ownerId, 146, "Myr Superion", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 0655b19af9b..0fd3ebad7e7 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -47,6 +47,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ManaEvent; +import mage.game.stack.Spell; /** * @@ -130,7 +131,10 @@ public class ManaPool implements Serializable { for (ManaPoolItem mana : manaItems) { if (filter != null) { if (!filter.match(mana.getSourceObject(), game)) { - continue; + // Prevent that cost reduction by convoke is filtered out + if (!(mana.getSourceObject() instanceof Spell) || ability.getSourceId().equals(mana.getSourceId())) { + continue; + } } } if (!manaType.equals(unlockedManaType) && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) {