From d51b2e9d058368db3afed81429358d615399a7bc Mon Sep 17 00:00:00 2001 From: jjdelasheras Date: Thu, 27 Aug 2015 12:34:57 +0200 Subject: [PATCH 01/39] Create GempalmPolluter.java --- .../mage/sets/legions/GempalmPolluter.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/legions/GempalmPolluter.java diff --git a/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java b/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java new file mode 100644 index 00000000000..52da1efc5af --- /dev/null +++ b/Mage.Sets/src/mage/sets/legions/GempalmPolluter.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.legions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author fireshoes + */ +public class GempalmPolluter extends CardImpl { + + static final private FilterPermanent filter = new FilterPermanent("Zombie"); + + static { + filter.add(new SubtypePredicate("Zombie")); + } + + public GempalmPolluter(UUID ownerId) { + super(ownerId, 70, "Gempalm Avenger", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{5}{B}"); + this.expansionSetCode = "LGN"; + this.subtype.add("Zombie"); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Cycling {B}{B} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{B}{B}"))); + + // When you cycle Gempalm Polluter, Target player loses X lifes where X is the number of zombies in game. + Effect effect = new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter)); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + public GempalmPolluter(final GempalPolluter card) { + super(card); + } + + @Override + public GempalmPolluter copy() { + return new GempalmPolluter(this); + } +} From 34d604fe4b8485b7bb8dbc1732d99bb937d2881e Mon Sep 17 00:00:00 2001 From: LoneFox Date: Wed, 9 Sep 2015 12:14:54 +0300 Subject: [PATCH 02/39] Rename BecomesChosenNonWallCreatureTypeTargetEffect -> BecomesChosenCreatureTypeTargetEffect and make the non-Wall part a parameter. Clean up the cards that use it. Fix Standardize, which was using wrong effect. --- .../sets/apocalypse/UnnaturalSelection.java | 18 +--- .../src/mage/sets/onslaught/Imagecrafter.java | 20 +---- .../mage/sets/onslaught/MistformMutant.java | 18 +--- .../src/mage/sets/onslaught/Standardize.java | 42 +++++---- ...BecomesChosenCreatureTypeTargetEffect.java | 85 +++++++++++++++++++ ...ChosenNonWallCreatureTypeTargetEffect.java | 66 -------------- 6 files changed, 115 insertions(+), 134 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java delete mode 100644 Mage/src/mage/abilities/effects/common/continuous/BecomesChosenNonWallCreatureTypeTargetEffect.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/UnnaturalSelection.java b/Mage.Sets/src/mage/sets/apocalypse/UnnaturalSelection.java index 57e913f07b4..0106d3ca2fc 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/UnnaturalSelection.java +++ b/Mage.Sets/src/mage/sets/apocalypse/UnnaturalSelection.java @@ -33,25 +33,13 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BecomesChosenNonWallCreatureTypeTargetEffect; -import mage.abilities.effects.common.continuous.BecomesSubtypeTargetEffect; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeTargetEffect; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -64,7 +52,7 @@ public class UnnaturalSelection extends CardImpl { this.expansionSetCode = "APC"; // {1}: Choose a creature type other than Wall. Target creature becomes that type until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenNonWallCreatureTypeTargetEffect(), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenCreatureTypeTargetEffect(true), new GenericManaCost(1)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -77,4 +65,4 @@ public class UnnaturalSelection extends CardImpl { public UnnaturalSelection copy() { return new UnnaturalSelection(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/onslaught/Imagecrafter.java b/Mage.Sets/src/mage/sets/onslaught/Imagecrafter.java index cb3db6bc1dd..543086215f0 100644 --- a/Mage.Sets/src/mage/sets/onslaught/Imagecrafter.java +++ b/Mage.Sets/src/mage/sets/onslaught/Imagecrafter.java @@ -34,25 +34,13 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BecomesChosenNonWallCreatureTypeTargetEffect; -import mage.abilities.effects.common.continuous.BecomesSubtypeTargetEffect; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeTargetEffect; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -69,7 +57,7 @@ public class Imagecrafter extends CardImpl { this.toughness = new MageInt(1); // {tap}: Choose a creature type other than Wall. Target creature becomes that type until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenNonWallCreatureTypeTargetEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenCreatureTypeTargetEffect(true), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -82,5 +70,5 @@ public class Imagecrafter extends CardImpl { public Imagecrafter copy() { return new Imagecrafter(this); } - -} \ No newline at end of file + +} diff --git a/Mage.Sets/src/mage/sets/onslaught/MistformMutant.java b/Mage.Sets/src/mage/sets/onslaught/MistformMutant.java index 1573178e3aa..c9ce8287761 100644 --- a/Mage.Sets/src/mage/sets/onslaught/MistformMutant.java +++ b/Mage.Sets/src/mage/sets/onslaught/MistformMutant.java @@ -34,25 +34,13 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BecomesChosenNonWallCreatureTypeTargetEffect; -import mage.abilities.effects.common.continuous.BecomesSubtypeTargetEffect; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeTargetEffect; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -69,7 +57,7 @@ public class MistformMutant extends CardImpl { this.toughness = new MageInt(4); // {1}{U}: Choose a creature type other than Wall. Target creature becomes that type until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenNonWallCreatureTypeTargetEffect(), new ManaCostsImpl<>("{1}{U}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesChosenCreatureTypeTargetEffect(true), new ManaCostsImpl<>("{1}{U}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -82,4 +70,4 @@ public class MistformMutant extends CardImpl { public MistformMutant copy() { return new MistformMutant(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/onslaught/Standardize.java b/Mage.Sets/src/mage/sets/onslaught/Standardize.java index 760ae76aead..c2c6f8624d0 100644 --- a/Mage.Sets/src/mage/sets/onslaught/Standardize.java +++ b/Mage.Sets/src/mage/sets/onslaught/Standardize.java @@ -34,7 +34,6 @@ import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BecomesChosenNonWallCreatureTypeTargetEffect; import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect; import mage.cards.CardImpl; import mage.cards.repository.CardRepository; @@ -59,8 +58,8 @@ public class Standardize extends CardImpl { this.expansionSetCode = "ONS"; // Choose a creature type other than Wall. Each creature becomes that type until end of turn. - - this.getSpellAbility().addEffect(new BecomesChosenNonWallCreatureTypeTargetEffect()); + + this.getSpellAbility().addEffect(new StandardizeEffect()); } public Standardize(final Standardize card) { @@ -77,19 +76,19 @@ public class Standardize extends CardImpl { class StandardizeEffect extends OneShotEffect { - public StandardizeEffect() { - super(Outcome.BoostCreature); - staticText = "choose a creature type other than wall, each creature's type becomes that type until end of turn"; - - } - - public StandardizeEffect(final StandardizeEffect effect) { - super(effect); - } + public StandardizeEffect() { + super(Outcome.BoostCreature); + staticText = "choose a creature type other than wall, each creature's type becomes that type until end of turn"; - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + } + + public StandardizeEffect(final StandardizeEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); String chosenType = ""; if (player != null && permanent != null) { @@ -111,14 +110,13 @@ class StandardizeEffect extends OneShotEffect { game.addEffect(effect, source); return true; } - + } return false; - } - - @Override - public Effect copy() { - return new StandardizeEffect(this); - } + } + @Override + public Effect copy() { + return new StandardizeEffect(this); + } } diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java new file mode 100644 index 00000000000..67c07ee1c0e --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java @@ -0,0 +1,85 @@ +package mage.abilities.effects.common.continuous; + +import java.util.Set; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.repository.CardRepository; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { + + private final boolean nonWall; + + public BecomesChosenCreatureTypeTargetEffect() { + this(false); + } + + public BecomesChosenCreatureTypeTargetEffect(boolean nonWall) { + super(Outcome.BoostCreature); + this.nonWall = nonWall; + if(nonWall) { + staticText = "choose a creature type other than wall, target creature's type becomes that type until end of turn"; + } + else { + staticText = "target creature becomes the creature type of your choice until end of turn"; + } + + } + + public BecomesChosenCreatureTypeTargetEffect(final BecomesChosenCreatureTypeTargetEffect effect) { + super(effect); + this.nonWall = effect.nonWall; + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getSourceId()); + String chosenType = ""; + if (player != null && card != null) { + Choice typeChoice = new ChoiceImpl(true); + String msg = "Choose a creature type"; + if(nonWall) { + msg += " other than Wall"; + } + typeChoice.setMessage(msg); + Set types = CardRepository.instance.getCreatureTypes(); + if(nonWall) { + types.remove("Wall"); + } + typeChoice.setChoices(types); + while (!player.choose(Outcome.BoostCreature, typeChoice, game)) { + if (!player.canRespond()) { + return false; + } + } + game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); + chosenType = typeChoice.getChoice(); + if (chosenType != null && !chosenType.isEmpty()) { + // ADD TYPE TO TARGET + ContinuousEffect effect = new BecomesSubtypeTargetEffect(Duration.EndOfTurn, chosenType); + effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); + game.addEffect(effect, source); + return true; + } + + } + return false; + } + + @Override + public Effect copy() { + return new BecomesChosenCreatureTypeTargetEffect(this); + } + +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenNonWallCreatureTypeTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenNonWallCreatureTypeTargetEffect.java deleted file mode 100644 index 316ef45e413..00000000000 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesChosenNonWallCreatureTypeTargetEffect.java +++ /dev/null @@ -1,66 +0,0 @@ -package mage.abilities.effects.common.continuous; - -import java.util.Set; - -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; - -public class BecomesChosenNonWallCreatureTypeTargetEffect extends OneShotEffect { - - public BecomesChosenNonWallCreatureTypeTargetEffect() { - super(Outcome.BoostCreature); - staticText = "choose a creature type other than wall, target creature's type becomes that type until end of turn"; - - } - - public BecomesChosenNonWallCreatureTypeTargetEffect(final BecomesChosenNonWallCreatureTypeTargetEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - String chosenType = ""; - if (player != null && permanent != null) { - Choice typeChoice = new ChoiceImpl(true); - typeChoice.setMessage("Choose creature type other than Wall"); - Set types = CardRepository.instance.getCreatureTypes(); - types.remove("Wall"); - typeChoice.setChoices(types); - while (!player.choose(Outcome.BoostCreature, typeChoice, game)) { - if (!player.canRespond()) { - return false; - } - } - game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice()); - chosenType = typeChoice.getChoice(); - if (chosenType != null && !chosenType.isEmpty()) { - // ADD TYPE TO TARGET - ContinuousEffect effect = new BecomesSubtypeTargetEffect(Duration.EndOfTurn, chosenType); - effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); - game.addEffect(effect, source); - return true; - } - - } - return false; - } - - @Override - public Effect copy() { - return new BecomesChosenNonWallCreatureTypeTargetEffect(this); - } - -} From 45862acaee8e8f1907009e1f8a8901afbec77dee Mon Sep 17 00:00:00 2001 From: LoneFox Date: Wed, 9 Sep 2015 12:16:56 +0300 Subject: [PATCH 03/39] Implement the Charm cycle from Onslaught --- .../src/mage/sets/onslaught/FeverCharm.java | 83 +++++++++++++++++ .../src/mage/sets/onslaught/MiseryCharm.java | 86 ++++++++++++++++++ .../src/mage/sets/onslaught/PietyCharm.java | 89 ++++++++++++++++++ .../mage/sets/onslaught/TrickeryCharm.java | 74 +++++++++++++++ .../mage/sets/onslaught/VitalityCharm.java | 91 +++++++++++++++++++ .../permanent/AttachedToPredicate.java | 59 ++++++++++++ 6 files changed, 482 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/onslaught/FeverCharm.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/MiseryCharm.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/PietyCharm.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/TrickeryCharm.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/VitalityCharm.java create mode 100644 Mage/src/mage/filter/predicate/permanent/AttachedToPredicate.java diff --git a/Mage.Sets/src/mage/sets/onslaught/FeverCharm.java b/Mage.Sets/src/mage/sets/onslaught/FeverCharm.java new file mode 100644 index 00000000000..69737911466 --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/FeverCharm.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.onslaught; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class FeverCharm extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wizard creature"); + + static { + filter.add(new SubtypePredicate("Wizard")); + } + + public FeverCharm(UUID ownerId) { + super(ownerId, 202, "Fever Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "ONS"; + + // Choose one - Target creature gains haste until end of turn + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + // or target creature gets +2/+0 until end of turn + Mode mode = new Mode(); + mode.getEffects().add(new BoostTargetEffect(2, 0, Duration.EndOfTurn)); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + // or Fever Charm deals 3 damage to target Wizard creature. + mode = new Mode(); + mode.getEffects().add(new DamageTargetEffect(3)); + mode.getTargets().add(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addMode(mode); + } + + public FeverCharm(final FeverCharm card) { + super(card); + } + + @Override + public FeverCharm copy() { + return new FeverCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/MiseryCharm.java b/Mage.Sets/src/mage/sets/onslaught/MiseryCharm.java new file mode 100644 index 00000000000..d3cbf5e1a78 --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/MiseryCharm.java @@ -0,0 +1,86 @@ +/* + * 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.onslaught; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPermanent; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author LoneFox + */ +public class MiseryCharm extends CardImpl { + + private static final FilterPermanent filter1 = new FilterPermanent("Cleric"); + private static final FilterCard filter2 = new FilterCard("Cleric card from your graveyard"); + + static { + filter1.add(new SubtypePredicate("Cleric")); + filter2.add(new SubtypePredicate("Cleric")); + } + + public MiseryCharm(UUID ownerId) { + super(ownerId, 158, "Misery Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}"); + this.expansionSetCode = "ONS"; + + // Choose one - Destroy target Cleric + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter1)); + // or return target Cleric card from your graveyard to your hand + Mode mode = new Mode(); + mode.getEffects().add(new ReturnToHandTargetEffect()); + mode.getTargets().add(new TargetCardInYourGraveyard(filter2)); + this.getSpellAbility().addMode(mode); + // or target player loses 2 life. + mode = new Mode(); + mode.getEffects().add(new LoseLifeTargetEffect(2)); + mode.getTargets().add(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + } + + public MiseryCharm(final MiseryCharm card) { + super(card); + } + + @Override + public MiseryCharm copy() { + return new MiseryCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/PietyCharm.java b/Mage.Sets/src/mage/sets/onslaught/PietyCharm.java new file mode 100644 index 00000000000..6a0ab78137b --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/PietyCharm.java @@ -0,0 +1,89 @@ +/* + * 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.onslaught; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.AttachedToPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class PietyCharm extends CardImpl { + + private static final FilterPermanent filter1 = new FilterPermanent("Aura attached to a creature"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Soldier creature"); + + static { + filter1.add(new SubtypePredicate("Aura")); + filter1.add(new AttachedToPredicate(new FilterCreaturePermanent())); + filter2.add(new SubtypePredicate("Soldier")); + } + + public PietyCharm(UUID ownerId) { + super(ownerId, 49, "Piety Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); + this.expansionSetCode = "ONS"; + + // Choose one - Destroy target Aura attached to a creature + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter1)); + // or target Soldier creature gets +2/+2 until end of turn + Mode mode = new Mode(); + mode.getEffects().add(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); + mode.getTargets().add(new TargetCreaturePermanent(filter2)); + this.getSpellAbility().addMode(mode); + // or creatures you control gain vigilance until end of turn. + mode = new Mode(); + mode.getEffects().add(new GainAbilityAllEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("creatures you control"))); + this.getSpellAbility().addMode(mode); + } + + public PietyCharm(final PietyCharm card) { + super(card); + } + + @Override + public PietyCharm copy() { + return new PietyCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/TrickeryCharm.java b/Mage.Sets/src/mage/sets/onslaught/TrickeryCharm.java new file mode 100644 index 00000000000..5e2251ae6f1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/TrickeryCharm.java @@ -0,0 +1,74 @@ +/* + * 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.onslaught; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.LookLibraryControllerEffect; +import mage.abilities.effects.common.continuous.BecomesChosenCreatureTypeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class TrickeryCharm extends CardImpl { + + public TrickeryCharm(UUID ownerId) { + super(ownerId, 119, "Trickery Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); + this.expansionSetCode = "ONS"; + + // Choose one - Target creature gains flying until end of turn + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + // or target creature becomes the creature type of your choice until end of turn + Mode mode = new Mode(); + mode.getEffects().add(new BecomesChosenCreatureTypeTargetEffect()); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + // or look at the top four cards of your library, then put them back in any order. + mode = new Mode(); + mode.getEffects().add(new LookLibraryControllerEffect(4)); + this.getSpellAbility().addMode(mode); + } + + public TrickeryCharm(final TrickeryCharm card) { + super(card); + } + + @Override + public TrickeryCharm copy() { + return new TrickeryCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/VitalityCharm.java b/Mage.Sets/src/mage/sets/onslaught/VitalityCharm.java new file mode 100644 index 00000000000..eee200ea12c --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/VitalityCharm.java @@ -0,0 +1,91 @@ +/* + * 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.onslaught; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.RegenerateTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.permanent.token.InsectToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class VitalityCharm extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Beast"); + + static { + filter.add(new SubtypePredicate("Beast")); + } + + public VitalityCharm(UUID ownerId) { + super(ownerId, 296, "Vitality Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); + this.expansionSetCode = "ONS"; + + // Choose one - Put a 1/1 green Insect creature token onto the battlefield + this.getSpellAbility().addEffect(new CreateTokenEffect(new InsectToken())); + // or target creature gets +1/+1 and gains trample until end of turn + Mode mode = new Mode(); + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); + effect.setText("target creature gets +1/+1"); + mode.getEffects().add(effect); + effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); + effect.setText("and gains trample until end of turn"); + mode.getEffects().add(effect); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + // or regenerate target Beast. + mode = new Mode(); + mode.getEffects().add(new RegenerateTargetEffect()); + mode.getTargets().add(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + } + + public VitalityCharm(final VitalityCharm card) { + super(card); + } + + @Override + public VitalityCharm copy() { + return new VitalityCharm(this); + } +} diff --git a/Mage/src/mage/filter/predicate/permanent/AttachedToPredicate.java b/Mage/src/mage/filter/predicate/permanent/AttachedToPredicate.java new file mode 100644 index 00000000000..7d155ffa14e --- /dev/null +++ b/Mage/src/mage/filter/predicate/permanent/AttachedToPredicate.java @@ -0,0 +1,59 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.filter.predicate.permanent; + +import java.util.UUID; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LoneFox + */ +public class AttachedToPredicate implements Predicate { + + private FilterPermanent filter; + + public AttachedToPredicate(FilterPermanent filter) { + this.filter = filter; + } + + @Override + public boolean apply(Permanent input, Game game) { + UUID attachedTo = input.getAttachedTo(); + return attachedTo != null && filter.match(game.getPermanent(attachedTo), game); + } + + @Override + public String toString() { + return "attached to " + filter.getMessage(); + } + +} From 6125b85be3782576b237a3131443ec722a407106 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Wed, 9 Sep 2015 13:38:21 +0300 Subject: [PATCH 04/39] Implement more Charms from Mirage and Visions --- .../src/mage/sets/mirage/ChaosCharm.java | 84 +++++++++++++++++ .../src/mage/sets/mirage/IvoryCharm.java | 74 +++++++++++++++ .../src/mage/sets/mirage/SeedlingCharm.java | 92 +++++++++++++++++++ .../src/mage/sets/visions/HearthCharm.java | 87 ++++++++++++++++++ .../src/mage/sets/visions/HopeCharm.java | 85 +++++++++++++++++ 5 files changed, 422 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirage/ChaosCharm.java create mode 100644 Mage.Sets/src/mage/sets/mirage/IvoryCharm.java create mode 100644 Mage.Sets/src/mage/sets/mirage/SeedlingCharm.java create mode 100644 Mage.Sets/src/mage/sets/visions/HearthCharm.java create mode 100644 Mage.Sets/src/mage/sets/visions/HopeCharm.java diff --git a/Mage.Sets/src/mage/sets/mirage/ChaosCharm.java b/Mage.Sets/src/mage/sets/mirage/ChaosCharm.java new file mode 100644 index 00000000000..5f99b6cd5f6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/ChaosCharm.java @@ -0,0 +1,84 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class ChaosCharm extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Wall"); + + static { + filter.add(new SubtypePredicate("Wall")); + } + + public ChaosCharm(UUID ownerId) { + super(ownerId, 163, "Chaos Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "MIR"; + + // Choose one - Destroy target Wall + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + // or Chaos Charm deals 1 damage to target creature + Mode mode = new Mode(); + mode.getEffects().add(new DamageTargetEffect(1)); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + // or target creature gains haste until end of turn. + mode = new Mode(); + mode.getEffects().add(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + public ChaosCharm(final ChaosCharm card) { + super(card); + } + + @Override + public ChaosCharm copy() { + return new ChaosCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/IvoryCharm.java b/Mage.Sets/src/mage/sets/mirage/IvoryCharm.java new file mode 100644 index 00000000000..50d7e9be957 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/IvoryCharm.java @@ -0,0 +1,74 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.PreventDamageToTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetCreatureOrPlayer; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class IvoryCharm extends CardImpl { + + public IvoryCharm(UUID ownerId) { + super(ownerId, 227, "Ivory Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); + this.expansionSetCode = "MIR"; + + // Choose one - All creatures get -2/-0 until end of turn + this.getSpellAbility().addEffect(new BoostAllEffect(-2, 0, Duration.EndOfTurn)); + // or tap target creature + Mode mode = new Mode(); + mode.getEffects().add(new TapTargetEffect()); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + // or prevent the next 1 damage that would be dealt to target creature or player this turn. + mode = new Mode(); + mode.getEffects().add(new PreventDamageToTargetEffect(Duration.EndOfTurn, 1)); + mode.getTargets().add(new TargetCreatureOrPlayer()); + this.getSpellAbility().addMode(mode); + } + + public IvoryCharm(final IvoryCharm card) { + super(card); + } + + @Override + public IvoryCharm copy() { + return new IvoryCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/SeedlingCharm.java b/Mage.Sets/src/mage/sets/mirage/SeedlingCharm.java new file mode 100644 index 00000000000..8b22af98f7f --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/SeedlingCharm.java @@ -0,0 +1,92 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.mirage; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Mode; +import mage.abilities.effects.common.RegenerateTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.AttachedToPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class SeedlingCharm extends CardImpl { + + private static final FilterPermanent filter1 = new FilterPermanent("Aura attached to a creature"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("green creature"); + + static { + filter1.add(new SubtypePredicate("Aura")); + filter1.add(new AttachedToPredicate(new FilterCreaturePermanent())); + filter2.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public SeedlingCharm(UUID ownerId) { + super(ownerId, 138, "Seedling Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); + this.expansionSetCode = "MIR"; + + // Choose one - Return target Aura attached to a creature to its owner's hand + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter1)); + // or regenerate target green creature + Mode mode = new Mode(); + mode.getEffects().add(new RegenerateTargetEffect()); + mode.getTargets().add(new TargetPermanent(filter2)); + this.getSpellAbility().addMode(mode); + // or target creature gains trample until end of turn. + mode = new Mode(); + mode.getEffects().add(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)); + mode.getTargets().add(new TargetCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + public SeedlingCharm(final SeedlingCharm card) { + super(card); + } + + @Override + public SeedlingCharm copy() { + return new SeedlingCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/HearthCharm.java b/Mage.Sets/src/mage/sets/visions/HearthCharm.java new file mode 100644 index 00000000000..43443a8296d --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/HearthCharm.java @@ -0,0 +1,87 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.Filter; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class HearthCharm extends CardImpl { + + private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("artifact creature"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("attacking creatures"); + private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent("creature with power 2 or less"); + static { + filter1.add(new CardTypePredicate(CardType.ARTIFACT)); + filter2.add(new AttackingPredicate()); + filter3.add(new PowerPredicate(Filter.ComparisonType.LessThan, 3)); + } + + public HearthCharm(UUID ownerId) { + super(ownerId, 82, "Hearth Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "VIS"; + + // Choose one - Destroy target artifact creature + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter1)); + // or attacking creatures get +1/+0 until end of turn + Mode mode = new Mode(); + mode.getEffects().add(new BoostAllEffect(1, 0, Duration.EndOfTurn, filter2, false)); + this.getSpellAbility().addMode(mode); + // or target creature with power 2 or less is unblockable this turn. + mode = new Mode(); + mode.getEffects().add(new CantBeBlockedTargetEffect()); + mode.getTargets().add(new TargetCreaturePermanent(filter3)); + this.getSpellAbility().addMode(mode); + } + + public HearthCharm(final HearthCharm card) { + super(card); + } + + @Override + public HearthCharm copy() { + return new HearthCharm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/HopeCharm.java b/Mage.Sets/src/mage/sets/visions/HopeCharm.java new file mode 100644 index 00000000000..bddab599788 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/HopeCharm.java @@ -0,0 +1,85 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPermanent; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class HopeCharm extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Aura"); + + static { + filter.add(new SubtypePredicate("Aura")); + } + + public HopeCharm(UUID ownerId) { + super(ownerId, 108, "Hope Charm", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); + this.expansionSetCode = "VIS"; + + // Choose one - Target creature gains first strike until end of turn + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + // or target player gains 2 life + Mode mode = new Mode(); + mode.getEffects().add(new GainLifeTargetEffect(2)); + mode.getTargets().add(new TargetPlayer()); + this.getSpellAbility().addMode(mode); + // or destroy target Aura. + mode = new Mode(); + mode.getEffects().add(new DestroyTargetEffect()); + mode.getTargets().add(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + } + + public HopeCharm(final HopeCharm card) { + super(card); + } + + @Override + public HopeCharm copy() { + return new HopeCharm(this); + } +} From aaef51a65af3eb843d95e7271779352756b38766 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Wed, 9 Sep 2015 13:38:08 -0500 Subject: [PATCH 05/39] [BFZ] Implemented Akoum Hellkite, Grovetender Druids, Grove Rumbler, Roil Spout, and Ulamog's Nullifier. Fixed Dominator Drone filter. --- .../sets/battleforzendikar/AkoumHellkite.java | 166 ++++++++++++++++++ .../battleforzendikar/DominatorDrone.java | 4 +- .../sets/battleforzendikar/GroveRumbler.java | 68 +++++++ .../battleforzendikar/GrovetenderDruids.java | 119 +++++++++++++ .../sets/battleforzendikar/RoilSpout.java | 64 +++++++ .../battleforzendikar/UlamogsNullifier.java | 136 ++++++++++++++ Utils/mtg-cards-data.txt | 5 + 7 files changed, 560 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/AkoumHellkite.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/GroveRumbler.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/GrovetenderDruids.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/RoilSpout.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/AkoumHellkite.java b/Mage.Sets/src/mage/sets/battleforzendikar/AkoumHellkite.java new file mode 100644 index 00000000000..8bafa7e7456 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/AkoumHellkite.java @@ -0,0 +1,166 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +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.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlayer; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author fireshoes + */ +public class AkoumHellkite extends CardImpl { + + public AkoumHellkite(UUID ownerId) { + super(ownerId, 139, "Akoum Hellkite", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Dragon"); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Landfall-Whenever a land enters the battlefield under you control, Akoum Hellkite deals 1 damage to target creature or player. + // If that land is a Mountain, Akoum Hellkite deals 2 damage to that creature or player instead. + Ability ability = new AkoumHellkiteTriggeredAbility(); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability); + } + + public AkoumHellkite(final AkoumHellkite card) { + super(card); + } + + @Override + public AkoumHellkite copy() { + return new AkoumHellkite(this); + } +} +class AkoumHellkiteTriggeredAbility extends TriggeredAbilityImpl { + + private static final String text = "Landfall - Whenever a land enters the battlefield under your control, {this} deals 1 damage to target creature or player. " + + "If that land is a Mountain, Akoum Hellkite deals 2 damage to that creature or player instead."; + + public AkoumHellkiteTriggeredAbility() { + super(Zone.BATTLEFIELD, new AkoumHellkiteDamageEffect()); + } + + public AkoumHellkiteTriggeredAbility(final AkoumHellkiteTriggeredAbility ability) { + super(ability); + } + + @Override + public AkoumHellkiteTriggeredAbility copy() { + return new AkoumHellkiteTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && permanent.getCardType().contains(CardType.LAND) + && permanent.getControllerId().equals(getControllerId())) { + Permanent sourcePermanent = game.getPermanent(getSourceId()); + if (sourcePermanent != null) + for (Effect effect : getEffects()) { + if (effect instanceof AkoumHellkiteDamageEffect) { + effect.setTargetPointer(new FixedTarget(permanent, game)); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return text; + } +} + +class AkoumHellkiteDamageEffect extends OneShotEffect { + + public AkoumHellkiteDamageEffect() { + super(Outcome.Damage); + } + + public AkoumHellkiteDamageEffect(final AkoumHellkiteDamageEffect effect) { + super(effect); + } + + @Override + public AkoumHellkiteDamageEffect copy() { + return new AkoumHellkiteDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent land = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Player player = game.getPlayer(source.getFirstTarget()); + if (land != null && player != null) { + if (land.hasSubtype("Mountain")) { + player.damage(2, source.getSourceId(), game, false, true); + } else { + player.damage(1, source.getSourceId(), game, false, true); + } + return true; + } + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (land != null && permanent != null) { + if (land.hasSubtype("Mountain")) { + permanent.damage(2, source.getSourceId(), game, false, true); + } else { + permanent.damage(1, source.getSourceId(), game, false, true); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/DominatorDrone.java b/Mage.Sets/src/mage/sets/battleforzendikar/DominatorDrone.java index c577d070c32..0d52800b415 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/DominatorDrone.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/DominatorDrone.java @@ -41,7 +41,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TargetController; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.filter.predicate.permanent.AnotherPredicate; @@ -51,7 +51,7 @@ import mage.filter.predicate.permanent.AnotherPredicate; */ public class DominatorDrone extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("another colorless creature"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another colorless creature"); static { filter.add(new AnotherPredicate()); diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/GroveRumbler.java b/Mage.Sets/src/mage/sets/battleforzendikar/GroveRumbler.java new file mode 100644 index 00000000000..830d1fddf12 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/GroveRumbler.java @@ -0,0 +1,68 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class GroveRumbler extends CardImpl { + + public GroveRumbler(UUID ownerId) { + super(ownerId, 211, "Grove Rumbler", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Elemental"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Landfall-Whenever a land enters the battlefield under your control, Grove Rumbler gets +2/+2 until end of turn. + this.addAbility(new LandfallAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); + } + + public GroveRumbler(final GroveRumbler card) { + super(card); + } + + @Override + public GroveRumbler copy() { + return new GroveRumbler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/GrovetenderDruids.java b/Mage.Sets/src/mage/sets/battleforzendikar/GrovetenderDruids.java new file mode 100644 index 00000000000..fa502b0d489 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/GrovetenderDruids.java @@ -0,0 +1,119 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AllyEntersBattlefieldTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.game.permanent.token.Token; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class GrovetenderDruids extends CardImpl { + + public GrovetenderDruids(UUID ownerId) { + super(ownerId, 212, "Grovetender Druids", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Elf"); + this.subtype.add("Druid"); + this.subtype.add("Ally"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Rally-Whenever Grovetender Druids or another Ally enters the battlefield under your control, you may pay {1}. + // If you do, put a 1/1 green Plant creature token onto the battlefield. + this.addAbility(new AllyEntersBattlefieldTriggeredAbility(new GrovetenderDruidsEffect(), false)); + } + + public GrovetenderDruids(final GrovetenderDruids card) { + super(card); + } + + @Override + public GrovetenderDruids copy() { + return new GrovetenderDruids(this); + } +} + +class GrovetenderDruidsEffect extends OneShotEffect { + + GrovetenderDruidsEffect() { + super(Outcome.Benefit); + this.staticText = "you may pay {1}. If you do, put a 1/1 green Plant creature token onto the battlefield"; + } + + GrovetenderDruidsEffect(final GrovetenderDruidsEffect effect) { + super(effect); + } + + @Override + public GrovetenderDruidsEffect copy() { + return new GrovetenderDruidsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if(player != null) { + if(player.chooseUse(Outcome.BoostCreature, "Do you want to to pay {1}?", source, game)) { + Cost cost = new ManaCostsImpl("{1}"); + if(cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { + new CreateTokenEffect(new GrovetenderDruidsPlantToken()).apply(game, source); + } + return true; + } + } + return false; + } +} + +class GrovetenderDruidsPlantToken extends Token { + + public GrovetenderDruidsPlantToken() { + super("Plant", "1/1 green Plant creature"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add("Plant"); + power = new MageInt(1); + toughness = new MageInt(1); + this.setOriginalExpansionSetCode("BFZ"); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/RoilSpout.java b/Mage.Sets/src/mage/sets/battleforzendikar/RoilSpout.java new file mode 100644 index 00000000000..6dd137db445 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/RoilSpout.java @@ -0,0 +1,64 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.AwakenAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class RoilSpout extends CardImpl { + + public RoilSpout(UUID ownerId) { + super(ownerId, 219, "Roil Spout", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{W}{U}"); + this.expansionSetCode = "BFZ"; + + // Put target creature on top of its owner's library. + this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Awaken 4-{4}{W}{U} + this.addAbility(new AwakenAbility(this, 4, "{4}{W}{U}")); + } + + public RoilSpout(final RoilSpout card) { + super(card); + } + + @Override + public RoilSpout copy() { + return new RoilSpout(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java new file mode 100644 index 00000000000..99f46579970 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java @@ -0,0 +1,136 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.DevoidAbility; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.other.OwnerPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetSpell; +import mage.target.common.TargetCardInExile; + +/** + * + * @author fireshoes + */ +public class UlamogsNullifier extends CardImpl { + + public UlamogsNullifier(UUID ownerId) { + super(ownerId, 207, "Ulamog's Nullifier", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Eldrazi"); + this.subtype.add("Processor"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Devoid + this.addAbility(new DevoidAbility(this.color)); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Ulamog's Nullifier enters the battlefield, you may put two cards your opponents own + // from exile into their owners' graveyards. If you do, counter target spell. + Effect effect = new UlamogsNullifierEffect(); + effect.setText("you may put two cards your opponents own from exile into their owners' graveyards. If you do, "); + Ability ability = new EntersBattlefieldAbility(effect, true); + ability.addEffect(new CounterTargetEffect()); + ability.addTarget(new TargetSpell()); + this.addAbility(ability); + } + + public UlamogsNullifier(final UlamogsNullifier card) { + super(card); + } + + @Override + public UlamogsNullifier copy() { + return new UlamogsNullifier(this); + } +} + +class UlamogsNullifierEffect extends OneShotEffect { + + private final static FilterCard filter = new FilterCard("cards your opponents own from exile"); + + static { + filter.add(new OwnerPredicate(TargetController.OPPONENT)); + } + + public UlamogsNullifierEffect() { + super(Outcome.Benefit); + this.staticText = "you may put two cards your opponents own from exile into their owners' graveyards. If you do, "; + } + + public UlamogsNullifierEffect(final UlamogsNullifierEffect effect) { + super(effect); + } + + @Override + public UlamogsNullifierEffect copy() { + return new UlamogsNullifierEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Target target = new TargetCardInExile(2, 2, filter, null); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + if (controller.chooseTarget(outcome, target, source, game)) { + Cards cardsToGraveyard = new CardsImpl(target.getTargets()); + controller.moveCards(cardsToGraveyard, null, Zone.GRAVEYARD, source, game); + return true; + } + } + } + return false; + } +} diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 06fed5fb28b..63248f5f3a3 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -27388,6 +27388,7 @@ Defiant Bloodlord|Battle for Zendikar|107|R|{5}{B}{B}|Creature - Vampire|4|5|Fly Ob Nixilis Reignited|Battle for Zendikar|119|M|{3}{B}{B}|Planeswalker - Nixilis|||+1: You draw a card and you lose 1 life.$-3: Destroy target creature.$-8: Target opponent gets an emblem with "Whenever a player draws a card, you lose 2 life."| Ruinous Path|Battle for Zendikar|123|R|{1}{B}{B}|Sorcery|||Destroy target creature or planeswalker.$Awaken 4-{5}{B}{B} (If you cast this spell for {5}{B}{B}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| Barrage Tyrant|Battle for Zendikar|127|R|{4}{R}|Creature - Eldrazi|5|3|Devoid (This card has no color.)${2}{R}, Sacrifice another colorless creature: Barrage Tyrant deals damage equal to the sacrificed creature's power to target creature or player.| +Akoum Hellkite|Battle for Zendikar|139|R|{4}{R}{R}|Creature - Dragon|4|4|Flying$Landfall-Whenever a land enters the battlefield under you control, Akoum Hellkite deals 1 damage to target creature or player. If that land is a Mountain, Akoum Hellkite deals 2 damage to that creature or player instead.| Radiant Flames|Battle for Zendikar|151|R|{2}{R}|Sorcery||Converge - Radiant Flames deals X damage to each creature, where X is the number of colors of mana spent to cast Radiant Flames.| Rolling Thunder|Battle for Zendikar|154|U|{X}{R}{R}|Sorcery|||Rolling Thunder deals X damage divided as you choose among any number of target creatures and/or players.| Zada, Hedron Grinder|Battle for Zendikar|162|R|{3}{R}|Legendary Creature - Goblin Ally|3|3|Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, copy that spell for each other creature you control that the spell could target. Each copy targets a different one of those creatures.| @@ -27400,9 +27401,13 @@ Undergrowth Champion|Battle for Zendikar|197|M|{1}{G}{G}|Creature - Elemental|2| Brood Butcher|Battle for Zendikar|199|R|{3}{B}{G}|Creature - Eldrazi Drone|3|3|Devoid (This card has no color.)$When Brood Butcher enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."${B}{G}, Sacrifice a creature: Target creature gets -2/-2 until end of turn.| Fathom Feeder|Battle for Zendikar|203|R|{U}{B}|Creature - Eldrazi Drone|1|1|Devoid (This card has no color.)$Deathtouch$Ingest Whenever this creature deals combat damage to a player, that player exiles the top card of his or her library.)${3}{U}{B}: Draw a card. Each opponent exiles the top card of his or her library.| Forerunner of Slaughter|Battle for Zendikar|204|U|{B}{R}|Creature - Eldrazi Drone|3|2|Devoid (This card has no color.)${1}: Target colorless creature gains haste until end of turn.| +Ulamog's Nullifier|Battle for Zendikar|207|U|{2}{U}{B}|Creature - Eldrazi Processor|2|3|Devoid (This card has no color.)$Flash$Flying$When Ulamog's Nullifier enters the battlefield, you may put two cards your opponents own from exile into their owners' graveyards. If you do, counter target spell.| Bring to Light|Battle for Zendikar|209|R|{3}{G}{U}|Sorcery|||Converge-Search your library for a creature, instant, or sorcery card with converted mana cost less than or equal to the number of colors of mana spent to cast Bring to Light, exile that card, then shuffle your library. You may cast that card without paying its mana cost.| +Grove Rumbler|Battle for Zendikar|211|U|{2}{R}{G}|Creature - Elemental|3|3|Trample$Landfall-Whenever a land enters the battlefield under your control, Grove Rumbler gets +2/+2 until end of turn.| +Grovetender Druids|Battle for Zendikar|212|U|{2}{G}{W}|Creature - Elf Druid Ally|3|3|Rally-Whenever Grovetender Druids or another Ally enters the battlefield under your control, you may pay {1}. If you do, put a 1/1 green Plant creature token onto the battlefield.| Kiora, Master of the Depths|Battle for Zendikar|213|M|{2}{G}{U}|Planeswalker - Kiora|||+1: Untap up to one target creature and up to one target land.$-2: Reveal the top four cards of your library. You may put a creature card and/or a land card from among them into your hand. Put the rest into your graveyard.$-8: You get an emblem with "Whenever a creature enters the battlefield under your control, you may have it fight target creature." Then put three 8/8 blue Octopus creature tokens onto the battlefield.| Omnath, Locus of Rage|Battle for Zendikar|217|M|{3}{R}{R}{G}{G}|Legendary Creature - Elemental|5|5|Landfall - Whenever a land enters the battlefield under your control, put a 5/5 red and green Elemental creature token onto the battlefield.$Whenever Omnath, Locus of Rage or another Elemental you control dies, Omnath deals 3 damage to target creature or player.| +Roil Spout|Battle for Zendikar|219|U|{1}{W}{U}|Sorcery|||Put target creature on top of its owner's library.$Awaken 4-{4}{W}{U} (If you cast this spell for {4}{W}{U}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| Veteran Warleader|Battle for Zendikar|221|R|{1}{G}{W}|Creature - Human Soldier Ally|0|0|Veteran Warleader's power and toughness are each equal to the number of creatures you control.$Tap another untapped Ally you control: Veteran Warleader gains your choice of first strike, vigilance, or trample until end of turn.| Hedron Archive|Battle for Zendikar|223|U|{4}|Artifact|||{T}: Add {2} to your mana pool.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| Canopy Vista|Battle for Zendikar|234|R||Land - Forest Plains|||Canopy Vista enters the battlefield tapped unless you control two or more basic lands.| From e009831da0a6d0e755e7e198c06dda65627f6737 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Wed, 9 Sep 2015 23:54:50 -0500 Subject: [PATCH 06/39] [BFZ] Fixed wrong ETB ability on Ulamog's Nullifier. --- .../src/mage/sets/battleforzendikar/UlamogsNullifier.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java index 99f46579970..4735a69964b 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java @@ -30,7 +30,7 @@ package mage.sets.battleforzendikar; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterTargetEffect; @@ -80,7 +80,7 @@ public class UlamogsNullifier extends CardImpl { // from exile into their owners' graveyards. If you do, counter target spell. Effect effect = new UlamogsNullifierEffect(); effect.setText("you may put two cards your opponents own from exile into their owners' graveyards. If you do, "); - Ability ability = new EntersBattlefieldAbility(effect, true); + Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); ability.addEffect(new CounterTargetEffect()); ability.addTarget(new TargetSpell()); this.addAbility(ability); From a61d67b9c237f1d208f591d210c071963990bb8a Mon Sep 17 00:00:00 2001 From: fireshoes Date: Thu, 10 Sep 2015 00:36:47 -0500 Subject: [PATCH 07/39] [BFZ] Fixed Ulamog's Nullifier countering spells if controller chose to move cards from exile to graveyard, but opponent didn't have cards in exile. --- .../src/mage/sets/battleforzendikar/UlamogsNullifier.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java index 4735a69964b..2f5fb4fea5e 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/UlamogsNullifier.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -48,6 +47,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.other.OwnerPredicate; import mage.game.Game; +import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; @@ -81,7 +81,6 @@ public class UlamogsNullifier extends CardImpl { Effect effect = new UlamogsNullifierEffect(); effect.setText("you may put two cards your opponents own from exile into their owners' graveyards. If you do, "); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); - ability.addEffect(new CounterTargetEffect()); ability.addTarget(new TargetSpell()); this.addAbility(ability); } @@ -121,12 +120,14 @@ class UlamogsNullifierEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + Spell spell = game.getStack().getSpell(source.getFirstTarget()); + if (controller != null && spell != null) { Target target = new TargetCardInExile(2, 2, filter, null); if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { if (controller.chooseTarget(outcome, target, source, game)) { Cards cardsToGraveyard = new CardsImpl(target.getTargets()); controller.moveCards(cardsToGraveyard, null, Zone.GRAVEYARD, source, game); + game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); return true; } } From e0521f1dcd274edc4dbf3fa0825aee4674841e5a Mon Sep 17 00:00:00 2001 From: Jesus Gabriel y Galan Date: Thu, 10 Sep 2015 15:55:08 +0200 Subject: [PATCH 08/39] new card: Duct Crawler --- .../src/mage/sets/stronghold/DuctCrawler.java | 72 +++++++++++++++++++ .../mage/sets/tenthedition/DuctCrawler.java | 52 ++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/stronghold/DuctCrawler.java create mode 100644 Mage.Sets/src/mage/sets/tenthedition/DuctCrawler.java diff --git a/Mage.Sets/src/mage/sets/stronghold/DuctCrawler.java b/Mage.Sets/src/mage/sets/stronghold/DuctCrawler.java new file mode 100644 index 00000000000..7bf0bd84f6b --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/DuctCrawler.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.stronghold; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedByTargetSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author anonymous + */ +public class DuctCrawler extends CardImpl { + + public DuctCrawler(UUID ownerId) { + super(ownerId, 79, "Duct Crawler", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); + this.expansionSetCode = "STH"; + this.subtype.add("Insect"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{R}: Target creature can't block Duct Crawler this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByTargetSourceEffect(Duration.EndOfTurn), + new ManaCostsImpl("{1}{R}")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public DuctCrawler(final DuctCrawler card) { + super(card); + } + + @Override + public DuctCrawler copy() { + return new DuctCrawler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tenthedition/DuctCrawler.java b/Mage.Sets/src/mage/sets/tenthedition/DuctCrawler.java new file mode 100644 index 00000000000..0998f89be03 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tenthedition/DuctCrawler.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.tenthedition; + +import java.util.UUID; + +/** + * + * @author anonymous + */ +public class DuctCrawler extends mage.sets.stronghold.DuctCrawler { + + public DuctCrawler(UUID ownerId) { + super(ownerId); + this.cardNumber = 198; + this.expansionSetCode = "10E"; + } + + public DuctCrawler(final DuctCrawler card) { + super(card); + } + + @Override + public DuctCrawler copy() { + return new DuctCrawler(this); + } +} From 040b970e59774d451442b4459c67c44ae122fad6 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Thu, 10 Sep 2015 13:19:15 -0500 Subject: [PATCH 09/39] [BFZ] Updated mtg-cards-data.txt for 9/10 spoilers. Implemented Greenwarden of Murasa, Felidar Sovereign reprint, Catacomb Sifter, Herald of Kozilek, Drana's Emissary, Shambling Vent, and Resolute Blademaster. --- .../battleforzendikar/CatacombSifter.java | 84 +++++++++++++ .../battleforzendikar/DranasEmissary.java | 77 ++++++++++++ .../battleforzendikar/FelidarSovereign.java | 54 +++++++++ .../GreenwardenOfMurasa.java | 111 ++++++++++++++++++ .../battleforzendikar/HeraldOfKozilek.java | 77 ++++++++++++ .../ResoluteBlademaster.java | 73 ++++++++++++ .../sets/battleforzendikar/ShamblingVent.java | 90 ++++++++++++++ Utils/mtg-cards-data.txt | 12 +- 8 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/CatacombSifter.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/DranasEmissary.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/FelidarSovereign.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/HeraldOfKozilek.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/ResoluteBlademaster.java create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/ShamblingVent.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/CatacombSifter.java b/Mage.Sets/src/mage/sets/battleforzendikar/CatacombSifter.java new file mode 100644 index 00000000000..7faeaddd544 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/CatacombSifter.java @@ -0,0 +1,84 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.DevoidAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.permanent.token.EldraziScionToken; + +/** + * + * @author fireshoes + */ +public class CatacombSifter extends CardImpl { + + private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature you control"); + static { + filter.add(new AnotherPredicate()); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public CatacombSifter(UUID ownerId) { + super(ownerId, 201, "Catacomb Sifter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Eldrazi"); + this.subtype.add("Drone"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Devoid + this.addAbility(new DevoidAbility(this.color)); + + // When Catacomb Sifter enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool." + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new EldraziScionToken()))); + + // Whenever another creature you control dies, scry 1 + this.addAbility(new DiesCreatureTriggeredAbility(new ScryEffect(1), false, filter)); + } + + public CatacombSifter(final CatacombSifter card) { + super(card); + } + + @Override + public CatacombSifter copy() { + return new CatacombSifter(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/DranasEmissary.java b/Mage.Sets/src/mage/sets/battleforzendikar/DranasEmissary.java new file mode 100644 index 00000000000..2b3b6c56394 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/DranasEmissary.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.sets.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; + +/** + * + * @author fireshoes + */ +public class DranasEmissary extends CardImpl { + + public DranasEmissary(UUID ownerId) { + super(ownerId, 210, "Drana's Emissary", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Vampire"); + this.subtype.add("Cleric"); + this.subtype.add("Ally"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your upkeep, each opponent loses 1 life and you gain 1 life. + Effect effect = new GainLifeEffect(1); + effect.setText("and you gain 1 life"); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new LoseLifeOpponentsEffect(1), TargetController.YOU, false); + ability.addEffect(effect); + this.addAbility(ability); + } + + public DranasEmissary(final DranasEmissary card) { + super(card); + } + + @Override + public DranasEmissary copy() { + return new DranasEmissary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/FelidarSovereign.java b/Mage.Sets/src/mage/sets/battleforzendikar/FelidarSovereign.java new file mode 100644 index 00000000000..4b5683b1f39 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/FelidarSovereign.java @@ -0,0 +1,54 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class FelidarSovereign extends mage.sets.zendikar.FelidarSovereign { + + public FelidarSovereign(UUID ownerId) { + super(ownerId); + this.cardNumber = 26; + this.expansionSetCode = "BFZ"; + this.rarity = Rarity.RARE; + } + + public FelidarSovereign(final FelidarSovereign card) { + super(card); + } + + @Override + public FelidarSovereign copy() { + return new FelidarSovereign(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java b/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java new file mode 100644 index 00000000000..697845c16a6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/GreenwardenOfMurasa.java @@ -0,0 +1,111 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class GreenwardenOfMurasa extends CardImpl { + + public GreenwardenOfMurasa(UUID ownerId) { + super(ownerId, 174, "Greenwarden of Murasa", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Elemental"); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // When Greenwarden of Murasa enters the battlefield, you may return target card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); + ability.addTarget(new TargetCardInYourGraveyard()); + this.addAbility(ability); + + // When Greenwarden of Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand. + ability = new DiesTriggeredAbility(new GreenwardenOfMurasaEffect(), false); + ability.addTarget(new TargetCardInYourGraveyard()); + this.addAbility(ability); + } + + public GreenwardenOfMurasa(final GreenwardenOfMurasa card) { + super(card); + } + + @Override + public GreenwardenOfMurasa copy() { + return new GreenwardenOfMurasa(this); + } +} + +class GreenwardenOfMurasaEffect extends OneShotEffect { + + public GreenwardenOfMurasaEffect() { + super(Outcome.Benefit); + this.staticText = "you may exile it. If you do, return target card from your graveyard to your hand"; + } + + public GreenwardenOfMurasaEffect(final GreenwardenOfMurasaEffect effect) { + super(effect); + } + + @Override + public GreenwardenOfMurasaEffect copy() { + return new GreenwardenOfMurasaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + if (controller.chooseUse(outcome, "Exile " + sourceObject.getLogName() + " to return card from your graveyard to your hand?", source, game)) { + new ExileSourceEffect().apply(game, source); + return new ReturnToHandTargetEffect().apply(game, source); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/HeraldOfKozilek.java b/Mage.Sets/src/mage/sets/battleforzendikar/HeraldOfKozilek.java new file mode 100644 index 00000000000..a5c74c30a41 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/HeraldOfKozilek.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.sets.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.DevoidAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorlessPredicate; + +/** + * + * @author fireshoes + */ +public class HeraldOfKozilek extends CardImpl { + + private static final FilterCard filter = new FilterCard("Colorless spells"); + + static { + filter.add(new ColorlessPredicate()); + } + + public HeraldOfKozilek(UUID ownerId) { + super(ownerId, 205, "Herald of Kozilek", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Eldrazi"); + this.subtype.add("Drone"); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Devoid + this.addAbility(new DevoidAbility(this.color)); + + // Colorless spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); + } + + public HeraldOfKozilek(final HeraldOfKozilek card) { + super(card); + } + + @Override + public HeraldOfKozilek copy() { + return new HeraldOfKozilek(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/ResoluteBlademaster.java b/Mage.Sets/src/mage/sets/battleforzendikar/ResoluteBlademaster.java new file mode 100644 index 00000000000..44e98d3a31f --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/ResoluteBlademaster.java @@ -0,0 +1,73 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AllyEntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class ResoluteBlademaster extends CardImpl { + + public ResoluteBlademaster(UUID ownerId) { + super(ownerId, 218, "Resolute Blademaster", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.subtype.add("Ally"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Rally-Whenever Resolute Blademaster or another Ally enters the battlefield under your control, + // you control gain double strike until end of turn. + Ability ability = new AllyEntersBattlefieldTriggeredAbility( + new GainAbilityAllEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, + new FilterControlledCreaturePermanent("creatures you control")), false); + this.addAbility(ability); + } + + public ResoluteBlademaster(final ResoluteBlademaster card) { + super(card); + } + + @Override + public ResoluteBlademaster copy() { + return new ResoluteBlademaster(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/ShamblingVent.java b/Mage.Sets/src/mage/sets/battleforzendikar/ShamblingVent.java new file mode 100644 index 00000000000..359b32ed5ee --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/ShamblingVent.java @@ -0,0 +1,90 @@ +/* + * 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.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.permanent.token.Token; + +/** + * + * @author fireshoes + */ +public class ShamblingVent extends CardImpl { + + public ShamblingVent(UUID ownerId) { + super(ownerId, 244, "Shambling Vent", Rarity.RARE, new CardType[]{CardType.LAND}, ""); + this.expansionSetCode = "BFZ"; + + // Shambling Vent enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W} or {B} to your mana pool. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + + // {1}{W}{B}: Shambling Vent becomes a 2/3 white and black Elemental creature with lifelink until end of turn. It's still a land. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect( + new ShamblingVentToken(), "land", Duration.EndOfTurn), new ManaCostsImpl("{1}{W}{B}"))); + } + + public ShamblingVent(final ShamblingVent card) { + super(card); + } + + @Override + public ShamblingVent copy() { + return new ShamblingVent(this); + } +} + +class ShamblingVentToken extends Token { + + public ShamblingVentToken() { + super("", "2/3 white and black Elemental creature with lifelink"); + cardType.add(CardType.CREATURE); + subtype.add("Elemental"); + color.setWhite(true); + color.setBlack(true); + power = new MageInt(2); + toughness = new MageInt(3); + addAbility(LifelinkAbility.getInstance()); + } +} \ No newline at end of file diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 63248f5f3a3..cb05a51c175 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -27358,7 +27358,7 @@ Mountain|Duel Decks: Zendikar vs. Eldrazi|74|L||Basic Land - Mountain|||M| Mountain|Duel Decks: Zendikar vs. Eldrazi|75|L||Basic Land - Mountain|||M| Blight Herder|Battle for Zendikar|2|R|{5}|Creature - Eldrazi Processor|4|5|When you cast Blight Herder, you may put two cards your opponents own from exile into their owners' graveyards. If you do, put three 1/1 colorless Eldrazi Scion creature tokens onto the battlefield. They have "Sacrifice this creature: Add {1} to your mana pool."| Breaker of Armies|Battle for Zendikar|3|U|{8}|Creature - Eldrazi|10|8|All creatures able to block Breaker of Armies do so.| -Conduit of Ruin|Battle for Zendikar|4|R|{6}|Creature - Eldrazi|5|5|When you cast Conduit of Ruin, you may search your library for a colorless creature card with converted mana cost 7 or greater. If you do, shuffle your library, then put that card on top.| +Conduit of Ruin|Battle for Zendikar|4|R|{6}|Creature - Eldrazi|5|5|When you cast Conduit of Ruin, you may search your library for a colorless creature card with converted mana cost 7 or greater, then shuffle your library and put that card on top of it. The first creature spell you cast each turn costs {2} less to cast.| Deathless Behemoth|Battle for Zendikar|5|R|{6}|Creature - Eldrazi|6|6|Vigilance$Sacrifice two Eldrazi Scions: Return Deathless Behemoth from your graveyard to your hand. Activate this ability only any time you could cast a sorcery.| Desolation Twin|Battle for Zendikar|6|R|{10}|Creature - Eldrazi|10|10|When you cast Desolation Twin, put a 10/10 colorless Eldrazi creature token onto the battlefield.| Eldrazi Devastator|Battle for Zendikar|7|C|{8}|Creature - Eldrazi|8|9|Trample| @@ -27366,7 +27366,9 @@ Kozilek's Channeler|Battle for Zendikar|10|C|{5}|Creature - Eldrazi|4|4|{T}: Add Oblivion Sower|Battle for Zendikar|11|M|{6}|Creature - Eldrazi|5|8|When you cast Oblivion Sower, target opponent exiles the top four cards of his or her library, then you may put any number of land cards that player owns from exile onto the battlefield under your control.| Titan's Presence|Battle for Zendikar|14|U|{3}|Instant|||As an additional cost to cast Titan's Presence, reveal a colorless creature card from your hand.$Exile target creature if its power is less than or equal to the revealed card's power.| Ulamog, the Ceaseless Hunger|Battle for Zendikar|15|M|{10}|Legendary Creature - Eldrazi|10|10|When you cast Ulamog, the Ceaseless Hunger, exile two target permanents.$Indestructible$Whenever Ulamog attacks, defending player exiles the top twenty cards of his or her library.| +Void Winnower|Battle for Zendikar|Battle for Zendikar|17|M|{9}|Your opponents can't cast spells with even converted mana costs. (Zero is even.)$Your opponents can't block with creatures with even converted mana costs.| Felidar Cub|Battle for Zendikar|25|C|{1}{W}|Creature - Cat Beast|2|2|Sacrifice Felidar Cub: Destroy target enchantment.| +Felidar Sovereign|Battle for Zendikar|26|R|{4}{W}{W}|Creature - Cat Beast|4|6|Vigilance, lifelink$At the beginning of your upkeep, if you have 40 or more life, you win the game.| Gideon, Ally of Zendikar|Battle for Zendikar|29|M|{2}{W}{W}|Planeswalker - Gideon|||+1: Until end of turn, Gideon, Ally of Zendikar becomes a 5/5 Human Soldier Ally creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn.$0: Put a 2/2 white Knight Ally creature token onto the battlefield.$-4: You get an emblem with "Creatures you control get +1/+1."| Gideon's Reproach|Battle for Zendikar|30|C|{1}{W}|Instant|||Gideon's Reproach deals 4 damage to target attacking or blocking creature.| Hero of Goma Fada|Battle for Zendikar|31|R|{4}{W}|Creature - Human Knight Ally|4|3|Rally - Whenever Hero of Goma Fada or another Ally enters the battlefield under your control, creatures you control gain indestructible until end of turn.| @@ -27392,6 +27394,7 @@ Akoum Hellkite|Battle for Zendikar|139|R|{4}{R}{R}|Creature - Dragon|4|4|Flying$ Radiant Flames|Battle for Zendikar|151|R|{2}{R}|Sorcery||Converge - Radiant Flames deals X damage to each creature, where X is the number of colors of mana spent to cast Radiant Flames.| Rolling Thunder|Battle for Zendikar|154|U|{X}{R}{R}|Sorcery|||Rolling Thunder deals X damage divided as you choose among any number of target creatures and/or players.| Zada, Hedron Grinder|Battle for Zendikar|162|R|{3}{R}|Legendary Creature - Goblin Ally|3|3|Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, copy that spell for each other creature you control that the spell could target. Each copy targets a different one of those creatures.| +Greenwarden of Murasa|Battle for Zendikar|174|M|{4}{G}{G}|Creature - Elemental|5|4|When Greenwarden of Murasa enters the battlefield, you may return target card from your graveyard to your hand.$When Greenwarden of Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand.| Nissa's Renewal|Battle for Zendikar|180|R|{5}{G}|Sorcery|||Search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. You gain 7 life.| Oran-Rief Hydra|Battle for Zendikar|181|R|{4}{G}{G}|Creature - Hydra|5|5|Trample$Landfall - Whenever a land enters the battlefield under your control, put a +1/+1 counter on Oran-Rief Hydra. If that land is a Forest, put two +1/+1 counters on Oran-Rief Hydra instead.| Retreat to Kazandu|Battle for Zendikar|186||U|{2}{G}|Enchantment|||Landfall-Whenever a land enters the battlefield under your control, choose one - Put a +1/+1 counter on target creature; or You gain 2 life.| @@ -27399,15 +27402,21 @@ Scythe Leopard|Battle for Zendikar|188|U|{G}|Creature - Cat|1|1|Landfall- Tajuru Warcaller|Battle for Zendikar|195|U|{3}{G}{G}|Creature - Elf Warrior Ally|2|1|Rally-Whenever Tajuru Warcaller or another Ally enters the battlefield under your control, creatures you control get +2/+2 until end of turn.| Undergrowth Champion|Battle for Zendikar|197|M|{1}{G}{G}|Creature - Elemental|2|2|If damage would be dealt to Undergrowth Champion while it has a +1/+1 counter on it, prevent that damage and remove a +1/+1 counter from Undergrowth Champion.$Landfall-Whenever a land enters the battlefield under your control, put a +1/+1 counter on Undergrowth Champion.| Brood Butcher|Battle for Zendikar|199|R|{3}{B}{G}|Creature - Eldrazi Drone|3|3|Devoid (This card has no color.)$When Brood Butcher enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."${B}{G}, Sacrifice a creature: Target creature gets -2/-2 until end of turn.| +Catacomb Sifter|Battle for Zendikar|201|U|{1}{B}{G}|Creature - Eldrazi Drone|2|3|Devoid (This card has no color.)$When Catacomb Sifter enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."$Whenever another creature you control dies, scry 1 (Look at the top card of your library. You may put that card on the bottom of your library.)| Fathom Feeder|Battle for Zendikar|203|R|{U}{B}|Creature - Eldrazi Drone|1|1|Devoid (This card has no color.)$Deathtouch$Ingest Whenever this creature deals combat damage to a player, that player exiles the top card of his or her library.)${3}{U}{B}: Draw a card. Each opponent exiles the top card of his or her library.| Forerunner of Slaughter|Battle for Zendikar|204|U|{B}{R}|Creature - Eldrazi Drone|3|2|Devoid (This card has no color.)${1}: Target colorless creature gains haste until end of turn.| +Herald of Kozilek|Battle for Zendikar|205|U|{1}{U}{R}|Creature - Eldrazi Drone|2|4|Devoid (This card has no color.)$Colorless spells you cast cost {1} less to cast.| Ulamog's Nullifier|Battle for Zendikar|207|U|{2}{U}{B}|Creature - Eldrazi Processor|2|3|Devoid (This card has no color.)$Flash$Flying$When Ulamog's Nullifier enters the battlefield, you may put two cards your opponents own from exile into their owners' graveyards. If you do, counter target spell.| Bring to Light|Battle for Zendikar|209|R|{3}{G}{U}|Sorcery|||Converge-Search your library for a creature, instant, or sorcery card with converted mana cost less than or equal to the number of colors of mana spent to cast Bring to Light, exile that card, then shuffle your library. You may cast that card without paying its mana cost.| +Drana's Emissary|Battle for Zendikar|210|U|{1}{W}{B}|Creature - Vampire Cleric Ally|2|2|Flying$At the beginning of your upkeep, each opponent loses 1 life and you gain 1 life.| Grove Rumbler|Battle for Zendikar|211|U|{2}{R}{G}|Creature - Elemental|3|3|Trample$Landfall-Whenever a land enters the battlefield under your control, Grove Rumbler gets +2/+2 until end of turn.| Grovetender Druids|Battle for Zendikar|212|U|{2}{G}{W}|Creature - Elf Druid Ally|3|3|Rally-Whenever Grovetender Druids or another Ally enters the battlefield under your control, you may pay {1}. If you do, put a 1/1 green Plant creature token onto the battlefield.| Kiora, Master of the Depths|Battle for Zendikar|213|M|{2}{G}{U}|Planeswalker - Kiora|||+1: Untap up to one target creature and up to one target land.$-2: Reveal the top four cards of your library. You may put a creature card and/or a land card from among them into your hand. Put the rest into your graveyard.$-8: You get an emblem with "Whenever a creature enters the battlefield under your control, you may have it fight target creature." Then put three 8/8 blue Octopus creature tokens onto the battlefield.| +Munda, Ambush Leader|Battle for Zendikar|215|R|{2}{R}{W}|Legendary Creature - Kor Ally|3|4|Haste$Rally-Whenever Munda, Ambush Leader or another Ally enters the battlefield under your control, you may look at the top four cards of your library. If you do, reveal any number of Ally cards from among them, then put those cards on top of your library in any order and the rest on the bottom in any order.| Omnath, Locus of Rage|Battle for Zendikar|217|M|{3}{R}{R}{G}{G}|Legendary Creature - Elemental|5|5|Landfall - Whenever a land enters the battlefield under your control, put a 5/5 red and green Elemental creature token onto the battlefield.$Whenever Omnath, Locus of Rage or another Elemental you control dies, Omnath deals 3 damage to target creature or player.| +Resolute Blademaster|Battle for Zendikar|218|U|{3}{R}{W}|Creature - Human Soldier Ally|2|2|Rally-Whenever Resolute Blademaster or another Ally enters the battlefield under your control, creatures you control gain double strike until end of turn.| Roil Spout|Battle for Zendikar|219|U|{1}{W}{U}|Sorcery|||Put target creature on top of its owner's library.$Awaken 4-{4}{W}{U} (If you cast this spell for {4}{W}{U}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| +Skyrider Elf|Battle for Zendikar|220|U|{X}{G}{U}|Creature - Elf Warrior Ally|0|0|Flying$Converge-Skyrider Elf enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.| Veteran Warleader|Battle for Zendikar|221|R|{1}{G}{W}|Creature - Human Soldier Ally|0|0|Veteran Warleader's power and toughness are each equal to the number of creatures you control.$Tap another untapped Ally you control: Veteran Warleader gains your choice of first strike, vigilance, or trample until end of turn.| Hedron Archive|Battle for Zendikar|223|U|{4}|Artifact|||{T}: Add {2} to your mana pool.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| Canopy Vista|Battle for Zendikar|234|R||Land - Forest Plains|||Canopy Vista enters the battlefield tapped unless you control two or more basic lands.| @@ -27415,6 +27424,7 @@ Cinder Glade|Battle for Zendikar|235|R||Land - Mountain Forest|||Cinder Glade en Evolving Wilds|Battle for Zendikar|236|C||Land|||{T}, Sacrifice Evolving Wilds: Search your library for a basic land card and put it onto the battlefield tapped. Then shuffle your library.| Lumbering Falls|Battle for Zendikar|239|R||Land|||Lumbering Falls enters the battlefield tapped.${T}: Add {G} or {U} to your mana pool.${2}{G}{U}: Lumbering Falls becomes a 3/3 green and blue Elemental creature with hexproof until end of turn. It's still a land.| Prairie Stream|Battle for Zendikar|241|R||Land - Plains Island|||Prairie Stream enters the battlefield tapped unless you control two or more basic lands.| +Shambling Vent|Battle for Zendikar|244|R||Land|||Shambling Vent enters the battlefield tapped.${T}: Add {W} or {B} to your mana pool.${1}{W}{B}: Shambling Vent becomes a 2/3 white and black Elemental creature with lifelink until end of turn. It's still a land.| Shrine of the Forsaken Gods|Battle for Zendikar|245|R||Land|||{T}: Add {1} to your mana pool.${T}: Add {2} to your mana pool. Spend this mana only to cast colorless spells. Activate this ability only if you control seven or more lands.| Smoldering Marsh|Battle for Zendikar|247|R||Land - Swamp Mountain|||Smoldering Marsh enters the battlefield tapped unless you control two or more basic lands.| Sunken Hollow|Battle for Zendikar|248|R||Land - Island Swamp|||Sunken Hollow enters the battlefield tapped unless you control two or more basic lands.| From 5494f191b90851b9a9ac1723d62369ee826832b7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 10 Sep 2015 22:47:26 +0200 Subject: [PATCH 10/39] * Arcbond - Fixed that an exception was thrown if the ability triggered. --- .../src/mage/sets/fatereforged/Arcbond.java | 16 +++++------ .../CreateDelayedTriggeredAbilityEffect.java | 27 ++++++++++++------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/sets/fatereforged/Arcbond.java b/Mage.Sets/src/mage/sets/fatereforged/Arcbond.java index 5b12862c707..c79cd10e84d 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/Arcbond.java +++ b/Mage.Sets/src/mage/sets/fatereforged/Arcbond.java @@ -62,7 +62,7 @@ public class Arcbond extends CardImpl { this.expansionSetCode = "FRF"; // Choose target creature. Whenever that creature is dealt damage this turn, it deals that much damage to each other creature and each player. - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new ArcbondDelayedTriggeredAbility(), true)); + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new ArcbondDelayedTriggeredAbility(), true, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -117,8 +117,8 @@ class ArcbondDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(targetObject.getSourceId()) && - targetObject.getPermanentOrLKIBattlefield(game) != null) { + if (event.getTargetId().equals(targetObject.getSourceId()) + && targetObject.getPermanentOrLKIBattlefield(game) != null) { for (Effect effect : this.getEffects()) { effect.setValue("damage", event.getAmount()); } @@ -139,21 +139,21 @@ class ArcbondDelayedTriggeredAbility extends DelayedTriggeredAbility { } class ArcbondEffect extends OneShotEffect { - + public ArcbondEffect() { super(Outcome.Benefit); this.staticText = "it deals that much damage to each other creature and each player"; } - + public ArcbondEffect(final ArcbondEffect effect) { super(effect); } - + @Override public ArcbondEffect copy() { return new ArcbondEffect(this); } - + @Override public boolean apply(Game game, Ability source) { int damage = (Integer) this.getValue("damage"); @@ -167,7 +167,7 @@ class ArcbondEffect extends OneShotEffect { FilterPermanent filter = new FilterCreaturePermanent("each other creature"); filter.add(Predicates.not(new PermanentIdPredicate(sourceId))); return new DamageEverythingEffect(new StaticValue(damage), filter, sourceId).apply(game, source); - } + } return false; } } diff --git a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java index de8085730a6..5d56cd403c8 100644 --- a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java +++ b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.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.abilities.Ability; @@ -43,21 +42,28 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { protected DelayedTriggeredAbility ability; protected boolean copyTargets; + protected boolean initAbility; public CreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability) { this(ability, true); } public CreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability, boolean copyTargets) { + this(ability, copyTargets, false); + } + + public CreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability, boolean copyTargets, boolean initAbility) { super(ability.getEffects().get(0).getOutcome()); this.ability = ability; this.copyTargets = copyTargets; + this.initAbility = initAbility; } public CreateDelayedTriggeredAbilityEffect(final CreateDelayedTriggeredAbilityEffect effect) { super(effect); this.ability = effect.ability.copy(); this.copyTargets = effect.copyTargets; + this.initAbility = effect.initAbility; } @Override @@ -73,23 +79,26 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { delayedAbility.setSourceObject(source.getSourceObject(game), game); if (this.copyTargets) { if (source.getTargets().isEmpty()) { - for(Effect effect : delayedAbility.getEffects()) { + for (Effect effect : delayedAbility.getEffects()) { effect.setTargetPointer(targetPointer); } } else { delayedAbility.getTargets().addAll(source.getTargets()); - for(Effect effect : delayedAbility.getEffects()) { + for (Effect effect : delayedAbility.getEffects()) { effect.getTargetPointer().init(game, source); } } } + if (initAbility) { + delayedAbility.init(game); + } game.addDelayedTriggeredAbility(delayedAbility); return true; } @Override public String getText(Mode mode) { - if (staticText != null && !staticText.isEmpty()){ + if (staticText != null && !staticText.isEmpty()) { return staticText; } if (ability.getRuleVisible()) { From bc7d158f50be6be1df46072179a99e4354a2a711 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 10 Sep 2015 22:58:06 +0200 Subject: [PATCH 11/39] * Gempalm Avenger - Fixed incomplete pull request. --- .../mage/sets/legions/GempalmPolluter.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java b/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java index 52da1efc5af..1bf3c4306e7 100644 --- a/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java +++ b/Mage.Sets/src/mage/sets/legions/GempalmPolluter.java @@ -32,17 +32,16 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.BoostAllEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.CyclingAbility; -import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Rarity; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPlayer; /** * @@ -50,11 +49,11 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class GempalmPolluter extends CardImpl { - static final private FilterPermanent filter = new FilterPermanent("Zombie"); + static final private FilterPermanent filter = new FilterPermanent("Zombie"); - static { - filter.add(new SubtypePredicate("Zombie")); - } + static { + filter.add(new SubtypePredicate("Zombie")); + } public GempalmPolluter(UUID ownerId) { super(ownerId, 70, "Gempalm Avenger", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{5}{B}"); @@ -66,13 +65,15 @@ public class GempalmPolluter extends CardImpl { // Cycling {B}{B} this.addAbility(new CyclingAbility(new ManaCostsImpl("{B}{B}"))); - // When you cycle Gempalm Polluter, Target player loses X lifes where X is the number of zombies in game. + // When you cycle Gempalm Polluter, you may have target player lose life equal to the number of Zombies on the battlefield. Effect effect = new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter)); + effect.setText("you may have target player lose life equal to the number of Zombies on the battlefield"); + Ability ability = new CycleTriggeredAbility(effect, true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } - public GempalmPolluter(final GempalPolluter card) { + public GempalmPolluter(final GempalmPolluter card) { super(card); } From 464955bd3aa5bf32f36d570b8d9bc6e0e053d61a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 10 Sep 2015 23:59:53 +0200 Subject: [PATCH 12/39] Fixed that for enchantments put into play (e.g. by Zur the Enchanter) that the selection of the enchanted permanent is not handled targeted. --- .../cards/triggers/ZurTheEnchanterTest.java | 84 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 10 ++- .../effects/AuraReplacementEffect.java | 5 +- 3 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZurTheEnchanterTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZurTheEnchanterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZurTheEnchanterTest.java new file mode 100644 index 00000000000..a0ce50e2430 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ZurTheEnchanterTest.java @@ -0,0 +1,84 @@ +/* + * 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.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ZurTheEnchanterTest extends CardTestPlayerBase { + + /** + * Zur the Enchanter's ability + shroud + * + * You can reproduce this by attacking with a Zur the Enchanter that has + * shroud (Lightning Greaves, Diplomatic Immunity, Greater Auramancy + an + * aura on him, etc.) and when his ability triggers searching for an aura + * (in this case, Empyrial Armor) and trying to attach it to Zur himself. + * The game won't allow you to attach it him, even though it should, since + * the enchantment is put onto the battlefield and not cast, hence, no + * targeting is done. The rulings page for Zur itself say it so on Gatherer: + * + * Shroud shouldn't stop Empyrial Armor from attaching to Zur, only + * something like protection from white, for example, would do that. + */ + @Test + public void testAuraToBattlefieldDoesNotTarget() { + // Flying + // Whenever Zur the Enchanter attacks, you may search your library for an enchantment card with converted mana cost 3 or less and put it onto the battlefield. If you do, shuffle your library. + addCard(Zone.BATTLEFIELD, playerB, "Zur the Enchanter"); // 1/4 + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Enchant creature + // Shroud (This permanent can't be the target of spells or abilities.) + // Enchanted creature has shroud. + addCard(Zone.HAND, playerB, "Diplomatic Immunity"); // {1}{U} + // Enchant creature + // Enchanted creature gets +1/+1 for each card in your hand. + addCard(Zone.LIBRARY, playerB, "Empyrial Armor"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diplomatic Immunity", "Zur the Enchanter"); + + attack(2, playerB, "Zur the Enchanter"); + setChoice(playerB, "Empyrial Armor"); + setChoice(playerB, "Zur the Enchanter"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Diplomatic Immunity", 1); + assertPermanentCount(playerB, "Empyrial Armor", 1); + assertPowerToughness(playerB, "Zur the Enchanter", 2, 5); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 51027f1e2f9..3abe238a3b0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -562,6 +562,11 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { if (!choices.isEmpty()) { + Ability source = null; + StackObject stackObject = game.getStack().getStackObject(sourceId); + if (stackObject != null) { + source = stackObject.getStackAbility(); + } if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer)) { // player target not implemted yet FilterPermanent filterPermanent; if (target instanceof TargetPermanentOrPlayer) { @@ -590,7 +595,8 @@ public class TestPlayer implements Player { continue; } if (permanent.getName().equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { + + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); targetFound = true; @@ -598,7 +604,7 @@ public class TestPlayer implements Player { } } } else if ((permanent.getName() + "-" + permanent.getExpansionSetCode()).equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); targetFound = true; diff --git a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java index 1a47387a473..eabe831a800 100644 --- a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java @@ -116,11 +116,12 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } } - game.applyEffects(); // So continuousEffects are removed if previous effect of the same ability did move objects that cuase continuous effects + game.applyEffects(); // So continuousEffects are removed if previous effect of the same ability did move objects that cuase continuous effects if (targetId == null) { - Target target = card.getSpellAbility().getTargets().get(0); + Target target = card.getSpellAbility().getTargets().get(0).copy(); enchantCardInGraveyard = target instanceof TargetCardInGraveyard; if (target != null) { + target.setNotTarget(true); // always not target because this way it's not handled targeted target.clearChosen(); // neccessary if e.g. aura is blinked multiple times } Player player = game.getPlayer(card.getOwnerId()); From a7fd898fcccf85a57dd736a21ecdb31b0aade7c1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 00:13:48 +0200 Subject: [PATCH 13/39] * Fixed that activation of gained abilities of face down permanents (e.g. by Singing Bell Strike) did uninteded reveal the face down creature. --- Mage.Common/src/mage/view/CardView.java | 3 ++- Mage.Common/src/mage/view/GameView.java | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 6c90e5a66b4..008f6cd0b46 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -153,7 +153,7 @@ public class CardView extends SimpleCardView { * for morph / face down cards to know which player may see information for * the card * @param showFaceDownCard if true and the card is not on the battelfield, - * also a face dwon card is shown in the view dwon cards will be shown + * also a face down card is shown in the view down cards will be shown */ public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard) { super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null); @@ -191,6 +191,7 @@ public class CardView extends SimpleCardView { this.power = Integer.toString(card.getPower().getValue()); this.toughness = Integer.toString(card.getToughness().getValue()); this.cardTypes = card.getCardType(); + this.faceDown = ((Permanent) card).isFaceDown(game); } else { // this.hideInfo = true; return; diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index e15827f6e76..3bc9f70e81c 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -103,7 +103,12 @@ public class GameView implements Serializable { Card card = game.getCard(stackObject.getSourceId()); if (card != null) { if (object != null) { - stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), new CardView(card))); + if (object instanceof Permanent) { + boolean controlled = ((Permanent) object).getControllerId().equals(createdForPlayerId); + stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, ((Permanent) object).getName(), new CardView(((Permanent) object), game, controlled, false))); + } else { + stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), new CardView(card, game, false, false))); + } } else { stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", new CardView(card))); } From cb3408432115f3690136a19dd05164f1d521251a Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 11 Sep 2015 08:43:41 +0300 Subject: [PATCH 14/39] Add DestroyattachedEffect and use it for existing cards. Implement cards: Frozen Solid, Mortal Wound, and Parallax Dementia --- .../src/mage/sets/coldsnap/FrozenSolid.java | 77 ++++++++++++++++++ .../mage/sets/conflux/YokeOfTheDamned.java | 48 ++--------- .../mage/sets/nemesis/ParallaxDementia.java | 81 +++++++++++++++++++ .../sets/planechase2012/QuietDisrepair.java | 52 +++--------- .../src/mage/sets/scourge/FrozenSolid.java | 52 ++++++++++++ .../src/mage/sets/visions/MortalWound.java | 73 +++++++++++++++++ .../src/mage/sets/zendikar/MireBlight.java | 50 +----------- .../effects/common/DestroyAttachedEffect.java | 78 ++++++++++++++++++ 8 files changed, 380 insertions(+), 131 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/coldsnap/FrozenSolid.java create mode 100644 Mage.Sets/src/mage/sets/nemesis/ParallaxDementia.java create mode 100644 Mage.Sets/src/mage/sets/scourge/FrozenSolid.java create mode 100644 Mage.Sets/src/mage/sets/visions/MortalWound.java create mode 100644 Mage/src/mage/abilities/effects/common/DestroyAttachedEffect.java diff --git a/Mage.Sets/src/mage/sets/coldsnap/FrozenSolid.java b/Mage.Sets/src/mage/sets/coldsnap/FrozenSolid.java new file mode 100644 index 00000000000..750dcca0a40 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/FrozenSolid.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.sets.coldsnap; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class FrozenSolid extends CardImpl { + + public FrozenSolid(UUID ownerId) { + super(ownerId, 35, "Frozen Solid", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{U}"); + this.expansionSetCode = "CSP"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature doesn't untap during its controller's untap step. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); + // When enchanted creature is dealt damage, destroy it. + this.addAbility(new DamageDealtToAttachedTriggeredAbility(new DestroyAttachedEffect("it"), false)); + } + + public FrozenSolid(final FrozenSolid card) { + super(card); + } + + @Override + public FrozenSolid copy() { + return new FrozenSolid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/conflux/YokeOfTheDamned.java b/Mage.Sets/src/mage/sets/conflux/YokeOfTheDamned.java index 3858801fa31..b26829d9e4e 100644 --- a/Mage.Sets/src/mage/sets/conflux/YokeOfTheDamned.java +++ b/Mage.Sets/src/mage/sets/conflux/YokeOfTheDamned.java @@ -30,16 +30,14 @@ package mage.sets.conflux; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -48,7 +46,7 @@ import mage.target.common.TargetCreaturePermanent; * @author jeffwadsworth */ public class YokeOfTheDamned extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); public YokeOfTheDamned(UUID ownerId) { @@ -60,13 +58,13 @@ public class YokeOfTheDamned extends CardImpl { // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // When a creature dies, destroy enchanted creature. - this.addAbility(new DiesCreatureTriggeredAbility(new DestroyEnchantedEffect(), false, filter)); - + this.addAbility(new DiesCreatureTriggeredAbility(new DestroyAttachedEffect("enchanted creature"), false, filter)); + } public YokeOfTheDamned(final YokeOfTheDamned card) { @@ -78,37 +76,3 @@ public class YokeOfTheDamned extends CardImpl { return new YokeOfTheDamned(this); } } - -class DestroyEnchantedEffect extends OneShotEffect { - - public DestroyEnchantedEffect() { - super(Outcome.Detriment); - staticText = "destroy enchanted creature"; - } - - public DestroyEnchantedEffect(final DestroyEnchantedEffect effect) { - super(effect); - } - - @Override - public DestroyEnchantedEffect copy() { - return new DestroyEnchantedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - UUID uuid = getTargetPointer().getFirst(game, source); - Permanent creature = game.getPermanent(uuid); - if (creature == null) { - creature = game.getPermanent(enchantment.getAttachedTo()); - } - if (creature != null) { - return creature.destroy(source.getSourceId(), game, false); - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/nemesis/ParallaxDementia.java b/Mage.Sets/src/mage/sets/nemesis/ParallaxDementia.java new file mode 100644 index 00000000000..4eb75225eb2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/nemesis/ParallaxDementia.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.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FadingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class ParallaxDementia extends CardImpl { + + public ParallaxDementia(UUID ownerId) { + super(ownerId, 62, "Parallax Dementia", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.expansionSetCode = "NMS"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Fading 1 + this.addAbility(new FadingAbility(1, this)); + // Enchanted creature gets +3/+2. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 2, Duration.WhileOnBattlefield))); + // When Parallax Dementia leaves the battlefield, destroy enchanted creature. That creature can't be regenerated. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new DestroyAttachedEffect("enchanted creature", true), false)); + } + + public ParallaxDementia(final ParallaxDementia card) { + super(card); + } + + @Override + public ParallaxDementia copy() { + return new ParallaxDementia(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planechase2012/QuietDisrepair.java b/Mage.Sets/src/mage/sets/planechase2012/QuietDisrepair.java index fd4fde99256..9257307fd9c 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/QuietDisrepair.java +++ b/Mage.Sets/src/mage/sets/planechase2012/QuietDisrepair.java @@ -29,24 +29,22 @@ package mage.sets.planechase2012; import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.keyword.EnchantAbility; -import mage.cards.CardImpl; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; /** @@ -76,7 +74,7 @@ public class QuietDisrepair extends CardImpl { this.addAbility(ability); // At the beginning of your upkeep, choose one - Destroy enchanted permanent; or you gain 2 life. - ability = new BeginningOfUpkeepTriggeredAbility(new QuietDisrepairDestroyEffect(), TargetController.YOU, false); + ability = new BeginningOfUpkeepTriggeredAbility(new DestroyAttachedEffect("enchanted permanent"), TargetController.YOU, false); Mode mode = new Mode(); mode.getEffects().add(new GainLifeEffect(2)); ability.addMode(mode); @@ -92,35 +90,3 @@ public class QuietDisrepair extends CardImpl { return new QuietDisrepair(this); } } - -class QuietDisrepairDestroyEffect extends OneShotEffect { - - public QuietDisrepairDestroyEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "Destroy enchanted permanent"; - } - - public QuietDisrepairDestroyEffect(final QuietDisrepairDestroyEffect effect) { - super(effect); - } - - @Override - public QuietDisrepairDestroyEffect copy() { - return new QuietDisrepairDestroyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment == null) { - enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (enchantment != null) { - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null) { - return enchanted.destroy(source.getSourceId(), game, false); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/scourge/FrozenSolid.java b/Mage.Sets/src/mage/sets/scourge/FrozenSolid.java new file mode 100644 index 00000000000..b90cf27b90b --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/FrozenSolid.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.scourge; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class FrozenSolid extends mage.sets.coldsnap.FrozenSolid { + + public FrozenSolid(UUID ownerId) { + super(ownerId); + this.cardNumber = 36; + this.expansionSetCode = "SCG"; + } + + public FrozenSolid(final FrozenSolid card) { + super(card); + } + + @Override + public FrozenSolid copy() { + return new FrozenSolid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/MortalWound.java b/Mage.Sets/src/mage/sets/visions/MortalWound.java new file mode 100644 index 00000000000..93b030b87c5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/MortalWound.java @@ -0,0 +1,73 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class MortalWound extends CardImpl { + + public MortalWound(UUID ownerId) { + super(ownerId, 63, "Mortal Wound", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{G}"); + this.expansionSetCode = "VIS"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // When enchanted creature is dealt damage, destroy it. + this.addAbility(new DamageDealtToAttachedTriggeredAbility(new DestroyAttachedEffect("it"), false)); + } + + public MortalWound(final MortalWound card) { + super(card); + } + + @Override + public MortalWound copy() { + return new MortalWound(this); + } +} diff --git a/Mage.Sets/src/mage/sets/zendikar/MireBlight.java b/Mage.Sets/src/mage/sets/zendikar/MireBlight.java index 9a6b87ebc59..f40db77d1ec 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MireBlight.java +++ b/Mage.Sets/src/mage/sets/zendikar/MireBlight.java @@ -29,22 +29,17 @@ package mage.sets.zendikar; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; import mage.abilities.keyword.EnchantAbility; 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.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -65,7 +60,7 @@ public class MireBlight extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // When enchanted creature is dealt damage, destroy it. - this.addAbility(new MireBlightTriggeredAbility()); + this.addAbility(new DamageDealtToAttachedTriggeredAbility(new DestroyAttachedEffect("it"), false)); } public MireBlight(final MireBlight card) { @@ -77,40 +72,3 @@ public class MireBlight extends CardImpl { return new MireBlight(this); } } - -class MireBlightTriggeredAbility extends TriggeredAbilityImpl { - - public MireBlightTriggeredAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect()); - } - - public MireBlightTriggeredAbility(final MireBlightTriggeredAbility ability) { - super(ability); - } - - @Override - public MireBlightTriggeredAbility copy() { - return new MireBlightTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - UUID targetId = event.getTargetId(); - if (enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(targetId)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When enchanted creature is dealt damage, destroy it."; - } -} diff --git a/Mage/src/mage/abilities/effects/common/DestroyAttachedEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAttachedEffect.java new file mode 100644 index 00000000000..68d51f9ad2f --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/DestroyAttachedEffect.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LoneFox + */ +public class DestroyAttachedEffect extends OneShotEffect { + + private final boolean noRegen; + + public DestroyAttachedEffect(String description) { + this(description, false); + } + + public DestroyAttachedEffect(String description, boolean noRegen) { + super(Outcome.DestroyPermanent); + this.noRegen = noRegen; + this.staticText = "destroy " + description; + if(noRegen) { + this.staticText += ". It can't be regenerated"; + } + } + + public DestroyAttachedEffect(final DestroyAttachedEffect effect) { + super(effect); + this.noRegen = effect.noRegen; + } + + @Override + public DestroyAttachedEffect copy() { + return new DestroyAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if(enchantment != null) { + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + if(enchanted != null) { + return enchanted.destroy(source.getSourceId(), game, noRegen); + } + } + return false; + } +} From dfb70e07a353e994e4eef1c7a5bdfe5398ef90fa Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 11 Sep 2015 09:24:40 +0300 Subject: [PATCH 15/39] Add description parameter to DontUntapInControllersUntapStepEnchantedEffect. This fixes a lot of tooltip texts that had "enchanted permanent" instead of "enchanted creature" in them. --- Mage.Sets/src/mage/sets/magic2013/Encrust.java | 8 ++++---- Mage.Sets/src/mage/sets/newphyrexia/NumbingDose.java | 2 +- Mage.Sets/src/mage/sets/planeshift/SleepingPotion.java | 5 +---- Mage.Sets/src/mage/sets/shardsofalara/ComaVeil.java | 4 ++-- ...DontUntapInControllersUntapStepEnchantedEffect.java | 10 +++++++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2013/Encrust.java b/Mage.Sets/src/mage/sets/magic2013/Encrust.java index 42e2c085d83..8eb28f4fb20 100644 --- a/Mage.Sets/src/mage/sets/magic2013/Encrust.java +++ b/Mage.Sets/src/mage/sets/magic2013/Encrust.java @@ -50,9 +50,9 @@ import mage.target.TargetPermanent; * @author jeffwadsworth */ public class Encrust extends CardImpl { - + private static final FilterPermanent filter = new FilterPermanent("artifact or creature"); - + static { filter.add(Predicates.or( new CardTypePredicate(CardType.CREATURE), @@ -71,9 +71,9 @@ public class Encrust extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // Enchanted permanent doesn't untap during its controller's untap step and its activated abilities can't be activated. - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect()); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect("permanent")); Effect effect = new CantActivateAbilitiesAttachedEffect(); effect.setText("and its activated abilities can't be activated"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/NumbingDose.java b/Mage.Sets/src/mage/sets/newphyrexia/NumbingDose.java index b4aad1e5299..ad3c61a53e2 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/NumbingDose.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/NumbingDose.java @@ -76,7 +76,7 @@ public class NumbingDose extends CardImpl { this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted permanent doesn't untap during its controller's untap step. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect("permanent"))); // At the beginning of the upkeep of enchanted permanent's controller, that player loses 1 life. this.addAbility(new NumbingDoseTriggeredAbility()); diff --git a/Mage.Sets/src/mage/sets/planeshift/SleepingPotion.java b/Mage.Sets/src/mage/sets/planeshift/SleepingPotion.java index ad0cacc5e64..3f794c585de 100644 --- a/Mage.Sets/src/mage/sets/planeshift/SleepingPotion.java +++ b/Mage.Sets/src/mage/sets/planeshift/SleepingPotion.java @@ -32,7 +32,6 @@ import mage.abilities.Ability; import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -66,9 +65,7 @@ public class SleepingPotion extends CardImpl { // When Sleeping Potion enters the battlefield, tap enchanted creature. this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); // Enchanted creature doesn't untap during its controller's untap step. - Effect effect = new DontUntapInControllersUntapStepEnchantedEffect(); - effect.setText("Enchanted creature doesn't untap during its controller's untap step"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); // When enchanted creature becomes the target of a spell or ability, sacrifice Sleeping Potion. this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect())); } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/ComaVeil.java b/Mage.Sets/src/mage/sets/shardsofalara/ComaVeil.java index bfbf3aacebe..9aea990c51f 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/ComaVeil.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/ComaVeil.java @@ -53,7 +53,7 @@ public class ComaVeil extends CardImpl { new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE))); } - + public ComaVeil(UUID ownerId) { super(ownerId, 36, "Coma Veil", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); this.expansionSetCode = "ALA"; @@ -66,7 +66,7 @@ public class ComaVeil extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); // Enchanted permanent doesn't untap during its controller's untap step. EnchantAbility ability = new EnchantAbility(auraTarget.getTargetName()); - ability.addEffect(new DontUntapInControllersUntapStepEnchantedEffect()); + ability.addEffect(new DontUntapInControllersUntapStepEnchantedEffect("permanent")); this.addAbility(ability); } diff --git a/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java b/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java index 7818cc0d242..a15aa089b8d 100644 --- a/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java +++ b/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java @@ -15,8 +15,12 @@ import mage.game.permanent.Permanent; public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRuleModifyingEffectImpl { public DontUntapInControllersUntapStepEnchantedEffect() { + this("creature"); + } + + public DontUntapInControllersUntapStepEnchantedEffect(String description) { super(Duration.WhileOnBattlefield, Outcome.Detriment, false, true); - staticText = "Enchanted permanent doesn't untap during its controller's untap step"; + staticText = "Enchanted " + description + " doesn't untap during its controller's untap step"; } public DontUntapInControllersUntapStepEnchantedEffect(final DontUntapInControllersUntapStepEnchantedEffect effect) { @@ -40,7 +44,7 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); if (enchanted != null) { return enchanted.getLogName() + " doesn't untap during its controller's untap step (" + enchantment.getLogName() + ")"; - } + } } return null; } @@ -49,7 +53,7 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { if (PhaseStep.UNTAP.equals(game.getTurn().getStepType())) { From f74558f94acb3458924ec056f6627fa40db198da Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 11 Sep 2015 10:50:55 +0300 Subject: [PATCH 16/39] Implement cards: Lightning Cloud, Serene Heart, Tranquil Domain, and Unfulfilled Desires --- .../src/mage/sets/mirage/SereneHeart.java | 66 +++++++++++++++++ .../src/mage/sets/mirage/TranquilDomain.java | 67 +++++++++++++++++ .../mage/sets/mirage/UnfulfilledDesires.java | 65 ++++++++++++++++ .../src/mage/sets/visions/LightningCloud.java | 74 +++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirage/SereneHeart.java create mode 100644 Mage.Sets/src/mage/sets/mirage/TranquilDomain.java create mode 100644 Mage.Sets/src/mage/sets/mirage/UnfulfilledDesires.java create mode 100644 Mage.Sets/src/mage/sets/visions/LightningCloud.java diff --git a/Mage.Sets/src/mage/sets/mirage/SereneHeart.java b/Mage.Sets/src/mage/sets/mirage/SereneHeart.java new file mode 100644 index 00000000000..bc493242d18 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/SereneHeart.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.mirage; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author LoneFox + */ +public class SereneHeart extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Auras"); + + static { + filter.add(new SubtypePredicate("Aura")); + } + + public SereneHeart(UUID ownerId) { + super(ownerId, 140, "Serene Heart", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); + this.expansionSetCode = "MIR"; + + // Destroy all Auras. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + public SereneHeart(final SereneHeart card) { + super(card); + } + + @Override + public SereneHeart copy() { + return new SereneHeart(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/TranquilDomain.java b/Mage.Sets/src/mage/sets/mirage/TranquilDomain.java new file mode 100644 index 00000000000..0e3bdd114fb --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/TranquilDomain.java @@ -0,0 +1,67 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author LoneFox + */ +public class TranquilDomain extends CardImpl { + + private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("non-Aura enchantments"); + + static { + filter.add(Predicates.not(new SubtypePredicate("Aura"))); + } + + public TranquilDomain(UUID ownerId) { + super(ownerId, 143, "Tranquil Domain", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); + this.expansionSetCode = "MIR"; + + // Destroy all non-Aura enchantments. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + public TranquilDomain(final TranquilDomain card) { + super(card); + } + + @Override + public TranquilDomain copy() { + return new TranquilDomain(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/UnfulfilledDesires.java b/Mage.Sets/src/mage/sets/mirage/UnfulfilledDesires.java new file mode 100644 index 00000000000..7d122a642e2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/UnfulfilledDesires.java @@ -0,0 +1,65 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.mirage; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class UnfulfilledDesires extends CardImpl { + + public UnfulfilledDesires(UUID ownerId) { + super(ownerId, 345, "Unfulfilled Desires", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{B}"); + this.expansionSetCode = "MIR"; + + // {1}, Pay 1 life: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new ManaCostsImpl("{1}")); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + } + + public UnfulfilledDesires(final UnfulfilledDesires card) { + super(card); + } + + @Override + public UnfulfilledDesires copy() { + return new UnfulfilledDesires(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/LightningCloud.java b/Mage.Sets/src/mage/sets/visions/LightningCloud.java new file mode 100644 index 00000000000..1f4309a4d81 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/LightningCloud.java @@ -0,0 +1,74 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author LoneFox + */ +public class LightningCloud extends CardImpl { + + private final static FilterSpell filter = new FilterSpell("a red spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public LightningCloud(UUID ownerId) { + super(ownerId, 87, "Lightning Cloud", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); + this.expansionSetCode = "VIS"; + + // Whenever a player casts a red spell, you may pay {R}. If you do, Lightning Cloud deals 1 damage to target creature or player. + Ability ability = new SpellCastAllTriggeredAbility(new DoIfCostPaid(new DamageTargetEffect(1), new ManaCostsImpl("{R}")), filter, false); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability); + } + + public LightningCloud(final LightningCloud card) { + super(card); + } + + @Override + public LightningCloud copy() { + return new LightningCloud(this); + } +} From d54e27a91719afb14648b896b7fda4a6f6436dfe Mon Sep 17 00:00:00 2001 From: LoneFox Date: Fri, 11 Sep 2015 11:40:14 +0300 Subject: [PATCH 17/39] Implement cards: Goblin Swine-Rider, Raging Gorilla, Unyaro Bee Sting, and Vitalizing Cascade --- .../src/mage/sets/mirage/UnyaroBeeSting.java | 60 ++++++++++++++ .../mage/sets/mirage/VitalizingCascade.java | 81 +++++++++++++++++++ .../mage/sets/visions/GoblinSwineRider.java | 64 +++++++++++++++ .../src/mage/sets/visions/RagingGorilla.java | 64 +++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mirage/UnyaroBeeSting.java create mode 100644 Mage.Sets/src/mage/sets/mirage/VitalizingCascade.java create mode 100644 Mage.Sets/src/mage/sets/visions/GoblinSwineRider.java create mode 100644 Mage.Sets/src/mage/sets/visions/RagingGorilla.java diff --git a/Mage.Sets/src/mage/sets/mirage/UnyaroBeeSting.java b/Mage.Sets/src/mage/sets/mirage/UnyaroBeeSting.java new file mode 100644 index 00000000000..1203dbd3163 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/UnyaroBeeSting.java @@ -0,0 +1,60 @@ +/* + * 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.mirage; + +import java.util.UUID; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author LoneFox + */ +public class UnyaroBeeSting extends CardImpl { + + public UnyaroBeeSting(UUID ownerId) { + super(ownerId, 148, "Unyaro Bee Sting", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{G}"); + this.expansionSetCode = "MIR"; + + // Unyaro Bee Sting deals 2 damage to target creature or player. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + } + + public UnyaroBeeSting(final UnyaroBeeSting card) { + super(card); + } + + @Override + public UnyaroBeeSting copy() { + return new UnyaroBeeSting(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirage/VitalizingCascade.java b/Mage.Sets/src/mage/sets/mirage/VitalizingCascade.java new file mode 100644 index 00000000000..13cb72b880e --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/VitalizingCascade.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.mirage; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.game.Game; + +/** + * + * @author LoneFox + */ +public class VitalizingCascade extends CardImpl { + + public VitalizingCascade(UUID ownerId) { + super(ownerId, 346, "Vitalizing Cascade", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{X}{G}{W}"); + this.expansionSetCode = "MIR"; + + // You gain X plus 3 life. + this.getSpellAbility().addEffect(new GainLifeEffect(new VitalizingCascadeValue())); + } + + public VitalizingCascade(final VitalizingCascade card) { + super(card); + } + + @Override + public VitalizingCascade copy() { + return new VitalizingCascade(this); + } +} + +class VitalizingCascadeValue extends ManacostVariableValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return super.calculate(game, sourceAbility, effect) + 3; + } + + @Override + public VitalizingCascadeValue copy() { + return new VitalizingCascadeValue(); + } + + @Override + public String toString() { + return "X plus 3"; + } +} + diff --git a/Mage.Sets/src/mage/sets/visions/GoblinSwineRider.java b/Mage.Sets/src/mage/sets/visions/GoblinSwineRider.java new file mode 100644 index 00000000000..34a515481e6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/GoblinSwineRider.java @@ -0,0 +1,64 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesBlockedTriggeredAbility; +import mage.abilities.effects.common.DamageAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterAttackingOrBlockingCreature; + +/** + * + * @author LoneFox + */ +public class GoblinSwineRider extends CardImpl { + + public GoblinSwineRider(UUID ownerId) { + super(ownerId, 81, "Goblin Swine-Rider", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); + this.expansionSetCode = "VIS"; + this.subtype.add("Goblin"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever Goblin Swine-Rider becomes blocked, it deals 2 damage to each attacking creature and each blocking creature. + this.addAbility(new BecomesBlockedTriggeredAbility(new DamageAllEffect(2, new FilterAttackingOrBlockingCreature("attacking creature and each blocking creature")), false)); + } + + public GoblinSwineRider(final GoblinSwineRider card) { + super(card); + } + + @Override + public GoblinSwineRider copy() { + return new GoblinSwineRider(this); + } +} diff --git a/Mage.Sets/src/mage/sets/visions/RagingGorilla.java b/Mage.Sets/src/mage/sets/visions/RagingGorilla.java new file mode 100644 index 00000000000..2f86b3c49bf --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/RagingGorilla.java @@ -0,0 +1,64 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class RagingGorilla extends CardImpl { + + public RagingGorilla(UUID ownerId) { + super(ownerId, 90, "Raging Gorilla", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.expansionSetCode = "VIS"; + this.subtype.add("Ape"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever Raging Gorilla blocks or becomes blocked, it gets +2/-2 until end of turn. + this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false)); + } + + public RagingGorilla(final RagingGorilla card) { + super(card); + } + + @Override + public RagingGorilla copy() { + return new RagingGorilla(this); + } +} From fe7dbcb5c22d14b696aab768258ef8b9f748798e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 14:19:28 +0200 Subject: [PATCH 18/39] * Dragonlord Silumgar - Added some additional information about the controlled target to hame log and permanent info. --- .../dragonsoftarkir/DragonlordSilumgar.java | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/DragonlordSilumgar.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/DragonlordSilumgar.java index 75905fc5777..72e12b1244f 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/DragonlordSilumgar.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/DragonlordSilumgar.java @@ -33,14 +33,21 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; -import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.Rarity; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.util.CardUtil; +import mage.util.GameLog; /** * @@ -59,17 +66,15 @@ public class DragonlordSilumgar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); - + // When Dragonlord Silumgar enters the battlefield, gain control of target creature or planeswalker for as long as you control Dragonlord Silumgar. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), null); - effect.setText("gain control of target creature or planeswalker for as long as you control {this}"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DragonlordSilumgarEffect(), false); ability.addTarget(new TargetCreatureOrPlaneswalker()); this.addAbility(ability); - + } public DragonlordSilumgar(final DragonlordSilumgar card) { @@ -81,3 +86,36 @@ public class DragonlordSilumgar extends CardImpl { return new DragonlordSilumgar(this); } } + +class DragonlordSilumgarEffect extends OneShotEffect { + + public DragonlordSilumgarEffect() { + super(Outcome.GainControl); + this.staticText = "gain control of target creature or planeswalker for as long as you control {this}"; + } + + public DragonlordSilumgarEffect(final DragonlordSilumgarEffect effect) { + super(effect); + } + + @Override + public DragonlordSilumgarEffect copy() { + return new DragonlordSilumgarEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller != null && sourcePermanent != null && target != null + && controller.getId().equals(sourcePermanent.getControllerId())) { + game.addEffect(new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), null), source); + if (!game.isSimulation()) { + game.informPlayers(sourcePermanent.getLogName() + ": " + controller.getLogName() + " gained control of " + target.getLogName()); + } + sourcePermanent.addInfo("gained control of", CardUtil.addToolTipMarkTags("Gained control of: " + GameLog.getColoredObjectIdNameForTooltip(target)), game); + } + return false; + } +} From 4c0473a3c9977615a1ca59af1ee20fd4c01079b9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 15:11:28 +0200 Subject: [PATCH 19/39] * Augur of Bolas - Fixed that the effect to reveal and put a card to hand was not optional. --- .../src/mage/sets/magic2013/AugurOfBolas.java | 73 +++++++++++++++++-- .../BecomesBasicLandTargetEffect.java | 19 +++-- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2013/AugurOfBolas.java b/Mage.Sets/src/mage/sets/magic2013/AugurOfBolas.java index dd963cf2758..7f1084a1331 100644 --- a/Mage.Sets/src/mage/sets/magic2013/AugurOfBolas.java +++ b/Mage.Sets/src/mage/sets/magic2013/AugurOfBolas.java @@ -28,16 +28,27 @@ package mage.sets.magic2013; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetCard; /** * @@ -46,6 +57,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; public class AugurOfBolas extends CardImpl { private static final FilterCard filter = new FilterCard("an instant or sorcery card"); + static { filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY))); } @@ -60,7 +72,7 @@ public class AugurOfBolas extends CardImpl { this.toughness = new MageInt(3); // When Augur of Bolas enters the battlefield, look at the top three cards of your library. You may reveal an instant or sorcery card from among them and put it into your hand. Put the rest on the bottom of your library in any order. - this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect(new StaticValue(3), false, new StaticValue(1), filter, false, true))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new AugurOfBolasEffect())); } public AugurOfBolas(final AugurOfBolas card) { @@ -72,3 +84,54 @@ public class AugurOfBolas extends CardImpl { return new AugurOfBolas(this); } } + +class AugurOfBolasEffect extends OneShotEffect { + + public AugurOfBolasEffect() { + super(Outcome.DrawCard); + this.staticText = "look at the top three cards of your library. You may reveal an instant or sorcery card from among them and put it into your hand. Put the rest on the bottom of your library in any order"; + } + + public AugurOfBolasEffect(final AugurOfBolasEffect effect) { + super(effect); + } + + @Override + public AugurOfBolasEffect copy() { + return new AugurOfBolasEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + Cards topCards = new CardsImpl(); + topCards.addAll(controller.getLibrary().getTopCards(game, 3)); + if (!topCards.isEmpty()) { + controller.lookAtCards(sourceObject.getIdName(), topCards, game); + int number = topCards.count(new FilterInstantOrSorceryCard(), source.getSourceId(), source.getControllerId(), game); + if (number > 0) { + if (controller.chooseUse(outcome, "Reveal an instant or sorcery card from the looked at cards and put it into your hand?", source, game)) { + Card card = null; + if (number == 1) { + card = topCards.getCards(new FilterInstantOrSorceryCard(), source.getSourceId(), source.getControllerId(), game).iterator().next(); + } else { + Target target = new TargetCard(Zone.LIBRARY, new FilterInstantOrSorceryCard()); + controller.chooseTarget(outcome, target, source, game); + card = topCards.get(target.getFirstTarget(), game); + } + if (card != null) { + controller.moveCards(card, null, Zone.HAND, source, game); + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); + topCards.remove(card); + } + } + controller.putCardsOnBottomOfLibrary(topCards, game, source, true); + } + } + return true; + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index 31a33d58316..44973fb990c 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.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 mage.abilities.effects.common.continuous; import java.util.ArrayList; @@ -53,8 +52,6 @@ import mage.players.Player; * * @author LevelX2 */ - - public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { protected boolean chooseLandType; @@ -98,6 +95,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { public BecomesBasicLandTargetEffect copy() { return new BecomesBasicLandTargetEffect(this); } + @Override public void init(Ability source, Game game) { super.init(source, game); @@ -113,12 +111,12 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } } - if(!loseOther) { + if (!loseOther) { for (UUID targetPermanent : targetPointer.getTargets(game, source)) { Permanent land = game.getPermanent(targetPermanent); if (land != null) { - for(String type : land.getSubtype()) { - if(!landTypes.contains(type)) { + for (String type : land.getSubtype()) { + if (!landTypes.contains(type)) { landTypes.add(type); } } @@ -135,7 +133,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: land.removeAllAbilities(source.getSourceId(), game); - for (String landType : landTypes) { + for (String landType : landTypes) { switch (landType) { case "Swamp": land.addAbility(new BlackManaAbility(), source.getSourceId(), game); @@ -156,6 +154,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } break; case TypeChangingEffects_4: + // Attention: Cards like Unstable Frontier that use this class do not give the "Basic" supertype to the target if (!land.getCardType().contains(CardType.LAND)) { land.getCardType().add(CardType.LAND); } @@ -180,8 +179,8 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } else { sb.append("Target land becomes a "); int i = 1; - for (String landType : landTypes) { - if (i >1) { + for (String landType : landTypes) { + if (i > 1) { if (i == landTypes.size()) { sb.append(" and "); } else { @@ -193,7 +192,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } } if (!duration.toString().isEmpty() && !duration.equals(Duration.EndOfGame)) { - sb.append(" ").append(duration.toString()); + sb.append(" ").append(duration.toString()); } return sb.toString(); } From 8050d5ba4cb7c986fd3ab9b30c6e6331d8525cf7 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Fri, 11 Sep 2015 12:13:20 -0500 Subject: [PATCH 20/39] [BFZ] Updated mtg-cards-data.txt for 9/11 spoilers. Implemented Akoum Firebird. --- .../sets/battleforzendikar/AkoumFirebird.java | 120 ++++++++++++++++++ Utils/mtg-cards-data.txt | 9 ++ 2 files changed, 129 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/battleforzendikar/AkoumFirebird.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/AkoumFirebird.java b/Mage.Sets/src/mage/sets/battleforzendikar/AkoumFirebird.java new file mode 100644 index 00000000000..d7333dbecc9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/battleforzendikar/AkoumFirebird.java @@ -0,0 +1,120 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.battleforzendikar; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksEachTurnStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author fireshoes + */ +public class AkoumFirebird extends CardImpl { + + public AkoumFirebird(UUID ownerId) { + super(ownerId, 138, "Akoum Firebird", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + this.expansionSetCode = "BFZ"; + this.subtype.add("Phoenix"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Akoum Firebird attacks each turn if able. + this.addAbility(new AttacksEachTurnStaticAbility()); + + // Landfall-Whenever a land enters the battlefield under your control, you may pay {4}{R}{R}. + // If you do, return Akoum Firebird from your graveyard to the battlefield. + this.addAbility(new AkoumFirebirdLandfallAbility(new DoIfCostPaid( + new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{4}{R}{R}")), false)); + } + + public AkoumFirebird(final AkoumFirebird card) { + super(card); + } + + @Override + public AkoumFirebird copy() { + return new AkoumFirebird(this); + } +} + +class AkoumFirebirdLandfallAbility extends TriggeredAbilityImpl { + + public AkoumFirebirdLandfallAbility(Effect effect, boolean optional) { + this(Zone.GRAVEYARD, effect, optional); + } + + public AkoumFirebirdLandfallAbility (Zone zone, Effect effect, Boolean optional ) { + super(zone, effect, optional); + } + + public AkoumFirebirdLandfallAbility(final AkoumFirebirdLandfallAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId); + } + + @Override + public String getRule() { + return "Landfall — Whenever a land enters the battlefield under your control, " + super.getRule(); + } + + @Override + public AkoumFirebirdLandfallAbility copy() { + return new AkoumFirebirdLandfallAbility(this); + } +} \ No newline at end of file diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index cb05a51c175..a82019c6502 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -27382,26 +27382,33 @@ Drowner of Hope|Battle for Zendikar|57|R|{5}{U}|Creature - Eldrazi|5|5|Devoid (This card has no color.)$Whenever Incubator Drone enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."| Mist Intruder|Battle for Zendikar|61|C|{1}{U}|Creature - Eldrazi Drone|1|2|Devoid (This card has no color.)$Flying$Ingest (Whenever this creature deals combat damage to a player, that player exiles the top card of his or her library.)| Coastal Discovery|Battle for Zendikar|73|U|{3}{U}|Sorcery|||Draw two cards.$Awaken 4 - {5}U} If you cast this spell for {5}{U}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| +Exert Influence|Battle for Zendikar|77|R|{4}{U}|Sorcery|||Converge-Gain control of target creature if its power is less than or equal to the number of colors spent to cast Exert Influence.| Guardian of Tazeem|Battle for Zendikar|78|R|{3}{U}{U}|Creature - Sphinx|4|5|Flying$Landfall - Whenever a land enters the battlefield under your control, tap target creature an opponent controls. If that land is an Island, that creature doesn't untap during its controller's next untap step.| +Prism Network|Battle for Zendikar|81|R|{4}{U}|Enchantment|||Converge-Prism Network enters the battlefield with hedron counters equal to the number of colors spent to cast Prism Network.$Remove a hedron counter from Prism Network: Tap target creature.${W}{U}{B}{R}{G}: Scry 3.| Dominator Drone|Battle for Zendikar|92|C|{2}{B}|Creature - Eldrazi Drone|3|2|Devoid (This card has no color.)$Ingest (Whenever this creature deals combat damage to a player, that player exiles the top card of his or her library.)$When Dominator Drone enters the battlefield, if you control another colorless creature, each opponent loses 2 life.| Skitterskin|Battle for Zendikar|97|U|{3}{B}|Creature - Eldrazi Drone|4|3|Devoid (This card has no color.)$Skitterskin can't block.${1}{B}: Regenerate Skitterskin. Activate this ability only if you control another colorless creature.| Smothering Abomination|Battle for Zendikar|99|R|{2}{B}{B}|Creature - Eldrazi|4|3|Devoid (This card has no color.)$Flying$At the beginning of your upkeep, sacrifice a creature.$Whenever you sacrifice a creature, draw a card.| +Transgress the Mind|Battle for Zendikar|101|U|{1}{B}|Sorcery|||Devoid (This card has no color.)$Target player reveals his or her hand. You may choose a card from it with converted mana cost 3 or greater and exile that card.| Defiant Bloodlord|Battle for Zendikar|107|R|{5}{B}{B}|Creature - Vampire|4|5|Flying$Whenever you gain life, target opponent loses that much life.| Ob Nixilis Reignited|Battle for Zendikar|119|M|{3}{B}{B}|Planeswalker - Nixilis|||+1: You draw a card and you lose 1 life.$-3: Destroy target creature.$-8: Target opponent gets an emblem with "Whenever a player draws a card, you lose 2 life."| Ruinous Path|Battle for Zendikar|123|R|{1}{B}{B}|Sorcery|||Destroy target creature or planeswalker.$Awaken 4-{5}{B}{B} (If you cast this spell for {5}{B}{B}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| Barrage Tyrant|Battle for Zendikar|127|R|{4}{R}|Creature - Eldrazi|5|3|Devoid (This card has no color.)${2}{R}, Sacrifice another colorless creature: Barrage Tyrant deals damage equal to the sacrificed creature's power to target creature or player.| +Akoum Firebird|Battle for Zendikar|138|M|{2}{R}{R}|Creature - Phoenix|3|3|Flying, haste$Akoum Firebird attacks each turn if able.$Landfall-Whenever a land enters the battlefield under your control, you may pay {4}{R}{R}. If you do, return Akoum Firebird from your graveyard to the battlefield.| Akoum Hellkite|Battle for Zendikar|139|R|{4}{R}{R}|Creature - Dragon|4|4|Flying$Landfall-Whenever a land enters the battlefield under you control, Akoum Hellkite deals 1 damage to target creature or player. If that land is a Mountain, Akoum Hellkite deals 2 damage to that creature or player instead.| Radiant Flames|Battle for Zendikar|151|R|{2}{R}|Sorcery||Converge - Radiant Flames deals X damage to each creature, where X is the number of colors of mana spent to cast Radiant Flames.| Rolling Thunder|Battle for Zendikar|154|U|{X}{R}{R}|Sorcery|||Rolling Thunder deals X damage divided as you choose among any number of target creatures and/or players.| Zada, Hedron Grinder|Battle for Zendikar|162|R|{3}{R}|Legendary Creature - Goblin Ally|3|3|Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, copy that spell for each other creature you control that the spell could target. Each copy targets a different one of those creatures.| +From Beyond|Battle for Zendikar|167|R|{3}{G}|Enchantment|||Devoid (This card has no color.)$At the beginning of your upkeep, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."${1}{G}, Sacrifice From Beyond: Search your library for an Eldrazi card, reveal it, put it into your hand, then shuffle your library.| Greenwarden of Murasa|Battle for Zendikar|174|M|{4}{G}{G}|Creature - Elemental|5|4|When Greenwarden of Murasa enters the battlefield, you may return target card from your graveyard to your hand.$When Greenwarden of Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand.| Nissa's Renewal|Battle for Zendikar|180|R|{5}{G}|Sorcery|||Search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. You gain 7 life.| Oran-Rief Hydra|Battle for Zendikar|181|R|{4}{G}{G}|Creature - Hydra|5|5|Trample$Landfall - Whenever a land enters the battlefield under your control, put a +1/+1 counter on Oran-Rief Hydra. If that land is a Forest, put two +1/+1 counters on Oran-Rief Hydra instead.| Retreat to Kazandu|Battle for Zendikar|186||U|{2}{G}|Enchantment|||Landfall-Whenever a land enters the battlefield under your control, choose one - Put a +1/+1 counter on target creature; or You gain 2 life.| Scythe Leopard|Battle for Zendikar|188|U|{G}|Creature - Cat|1|1|Landfall-Whenever a land enters the battlefield under your control, Scythe Leopard gets +1/+1 until end of turn.| +Sylvan Scrying|Battle for Zendikar|192|U|{1}{G}|Sorcery|||Search your library for a land card, reveal it, and put it into your hand. Then shuffle your library.| Tajuru Warcaller|Battle for Zendikar|195|U|{3}{G}{G}|Creature - Elf Warrior Ally|2|1|Rally-Whenever Tajuru Warcaller or another Ally enters the battlefield under your control, creatures you control get +2/+2 until end of turn.| Undergrowth Champion|Battle for Zendikar|197|M|{1}{G}{G}|Creature - Elemental|2|2|If damage would be dealt to Undergrowth Champion while it has a +1/+1 counter on it, prevent that damage and remove a +1/+1 counter from Undergrowth Champion.$Landfall-Whenever a land enters the battlefield under your control, put a +1/+1 counter on Undergrowth Champion.| Brood Butcher|Battle for Zendikar|199|R|{3}{B}{G}|Creature - Eldrazi Drone|3|3|Devoid (This card has no color.)$When Brood Butcher enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."${B}{G}, Sacrifice a creature: Target creature gets -2/-2 until end of turn.| +Brutal Expulsion|Battle for Zendikar|200|R|{2}{U}{R}|Instant|||Devoid (This card has no color.)$Choose one or both - Return target spell or creature to its owner's hand; or Brutal Expulsion deals 2 damage to target creature or planeswalker. If that permanent would be put into a graveyard this turn, exile it instead.| Catacomb Sifter|Battle for Zendikar|201|U|{1}{B}{G}|Creature - Eldrazi Drone|2|3|Devoid (This card has no color.)$When Catacomb Sifter enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool."$Whenever another creature you control dies, scry 1 (Look at the top card of your library. You may put that card on the bottom of your library.)| Fathom Feeder|Battle for Zendikar|203|R|{U}{B}|Creature - Eldrazi Drone|1|1|Devoid (This card has no color.)$Deathtouch$Ingest Whenever this creature deals combat damage to a player, that player exiles the top card of his or her library.)${3}{U}{B}: Draw a card. Each opponent exiles the top card of his or her library.| Forerunner of Slaughter|Battle for Zendikar|204|U|{B}{R}|Creature - Eldrazi Drone|3|2|Devoid (This card has no color.)${1}: Target colorless creature gains haste until end of turn.| @@ -27412,12 +27419,14 @@ Drana's Emissary|Battle for Zendikar|210|U|{1}{W}{B}|Creature - Vampire Cleric A Grove Rumbler|Battle for Zendikar|211|U|{2}{R}{G}|Creature - Elemental|3|3|Trample$Landfall-Whenever a land enters the battlefield under your control, Grove Rumbler gets +2/+2 until end of turn.| Grovetender Druids|Battle for Zendikar|212|U|{2}{G}{W}|Creature - Elf Druid Ally|3|3|Rally-Whenever Grovetender Druids or another Ally enters the battlefield under your control, you may pay {1}. If you do, put a 1/1 green Plant creature token onto the battlefield.| Kiora, Master of the Depths|Battle for Zendikar|213|M|{2}{G}{U}|Planeswalker - Kiora|||+1: Untap up to one target creature and up to one target land.$-2: Reveal the top four cards of your library. You may put a creature card and/or a land card from among them into your hand. Put the rest into your graveyard.$-8: You get an emblem with "Whenever a creature enters the battlefield under your control, you may have it fight target creature." Then put three 8/8 blue Octopus creature tokens onto the battlefield.| +March from the Tomb|Battle for Zendikar|214|R|{3}{W}{B}|Sorcery|||Return any number of target Ally creature cards with total converted mana cost of 8 or less from your graveyard to the battlefield.| Munda, Ambush Leader|Battle for Zendikar|215|R|{2}{R}{W}|Legendary Creature - Kor Ally|3|4|Haste$Rally-Whenever Munda, Ambush Leader or another Ally enters the battlefield under your control, you may look at the top four cards of your library. If you do, reveal any number of Ally cards from among them, then put those cards on top of your library in any order and the rest on the bottom in any order.| Omnath, Locus of Rage|Battle for Zendikar|217|M|{3}{R}{R}{G}{G}|Legendary Creature - Elemental|5|5|Landfall - Whenever a land enters the battlefield under your control, put a 5/5 red and green Elemental creature token onto the battlefield.$Whenever Omnath, Locus of Rage or another Elemental you control dies, Omnath deals 3 damage to target creature or player.| Resolute Blademaster|Battle for Zendikar|218|U|{3}{R}{W}|Creature - Human Soldier Ally|2|2|Rally-Whenever Resolute Blademaster or another Ally enters the battlefield under your control, creatures you control gain double strike until end of turn.| Roil Spout|Battle for Zendikar|219|U|{1}{W}{U}|Sorcery|||Put target creature on top of its owner's library.$Awaken 4-{4}{W}{U} (If you cast this spell for {4}{W}{U}, also put four +1/+1 counters on target land you control and it becomes a 0/0 Elemental creature with haste. It's still a land.)| Skyrider Elf|Battle for Zendikar|220|U|{X}{G}{U}|Creature - Elf Warrior Ally|0|0|Flying$Converge-Skyrider Elf enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.| Veteran Warleader|Battle for Zendikar|221|R|{1}{G}{W}|Creature - Human Soldier Ally|0|0|Veteran Warleader's power and toughness are each equal to the number of creatures you control.$Tap another untapped Ally you control: Veteran Warleader gains your choice of first strike, vigilance, or trample until end of turn.| +Circle of Hedrons|Battle for Zendikar|222|R|{4}|Artifact|||When Circle of Hedrons enters the battlefield, exile all creatures with power 5 or greater until Circle of Hedrons leaves the battlefield.| Hedron Archive|Battle for Zendikar|223|U|{4}|Artifact|||{T}: Add {2} to your mana pool.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.| Canopy Vista|Battle for Zendikar|234|R||Land - Forest Plains|||Canopy Vista enters the battlefield tapped unless you control two or more basic lands.| Cinder Glade|Battle for Zendikar|235|R||Land - Mountain Forest|||Cinder Glade enters the battlefield tapped unless you control two or more basic lands.| From 963f371c1233b2d72eb2765140ece8ce332fd46f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 21:50:23 +0200 Subject: [PATCH 21/39] * Anafenza the Foremost - Fixed that animated permanents (e.g. Lavaclaw Reaches) were not moved to exile if they died while beeing a creatures. --- .../src/mage/sets/guildpact/GruulWarPlow.java | 14 +++++--- .../khansoftarkir/AnafenzaTheForemost.java | 25 ++++++++------ .../src/mage/sets/prophecy/ChimericIdol.java | 2 +- .../mage/sets/worldwake/LavaclawReaches.java | 23 +++++++------ .../cards/triggers/TargetedTriggeredTest.java | 25 ++++++++++++++ .../test/commander/duel/AnafenzaTest.java | 33 +++++++++++++++++++ 6 files changed, 96 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/sets/guildpact/GruulWarPlow.java b/Mage.Sets/src/mage/sets/guildpact/GruulWarPlow.java index 9b5b7d3e080..9a53f409a58 100644 --- a/Mage.Sets/src/mage/sets/guildpact/GruulWarPlow.java +++ b/Mage.Sets/src/mage/sets/guildpact/GruulWarPlow.java @@ -28,10 +28,6 @@ package mage.sets.guildpact; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -40,6 +36,9 @@ import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; 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.game.permanent.token.Token; @@ -52,7 +51,11 @@ public class GruulWarPlow extends CardImpl { public GruulWarPlow(UUID ownerId) { super(ownerId, 151, "Gruul War Plow", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); this.expansionSetCode = "GPT"; + + // Creatures you control have trample. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent()))); + + // {1}{R}{G}: Gruul War Plow becomes a 4/4 Juggernaut artifact creature until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GruulWarPlowToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{1}{R}{G}"))); } @@ -67,6 +70,7 @@ public class GruulWarPlow extends CardImpl { } class GruulWarPlowToken extends Token { + GruulWarPlowToken() { super("Juggernaut", "4/4 Juggernaut artifact creature"); cardType.add(CardType.CREATURE); @@ -75,4 +79,4 @@ class GruulWarPlowToken extends Token { power = new MageInt(4); toughness = new MageInt(4); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java b/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java index f43b44cac72..232964e4733 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/AnafenzaTheForemost.java @@ -81,7 +81,7 @@ public class AnafenzaTheForemost extends CardImpl { this.addAbility(ability); // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AnafenzaTheForemostEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AnafenzaTheForemostEffect())); } public AnafenzaTheForemost(final AnafenzaTheForemost card) { @@ -119,16 +119,15 @@ class AnafenzaTheForemostEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - if (((ZoneChangeEvent)event).getFromZone().equals(Zone.BATTLEFIELD)) { - Permanent permanent = ((ZoneChangeEvent)event).getTarget(); + if (((ZoneChangeEvent) event).getFromZone().equals(Zone.BATTLEFIELD)) { + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); if (permanent != null) { return controller.moveCardToExileWithInfo(permanent, null, null, source.getSourceId(), game, Zone.BATTLEFIELD, true); } - } - else { + } else { Card card = game.getCard(event.getTargetId()); if (card != null) { - return controller.moveCardToExileWithInfo(card, null, null, source.getSourceId(), game, ((ZoneChangeEvent)event).getFromZone(), true); + return controller.moveCardToExileWithInfo(card, null, null, source.getSourceId(), game, ((ZoneChangeEvent) event).getFromZone(), true); } } } @@ -142,11 +141,17 @@ class AnafenzaTheForemostEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getToZone() == Zone.GRAVEYARD) { Card card = game.getCard(event.getTargetId()); - if (card != null && card.getCardType().contains(CardType.CREATURE) && - game.getOpponents(source.getControllerId()).contains(card.getOwnerId())) { - return true; + if (card != null && game.getOpponents(source.getControllerId()).contains(card.getOwnerId())) { // Anafenza only cares about cards + if (zEvent.getTarget() != null) { // if it comes from permanent, check if it was a creature on the battlefield + if (zEvent.getTarget().getCardType().contains(CardType.CREATURE)) { + return true; + } + } else if (card.getCardType().contains(CardType.CREATURE)) { + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/sets/prophecy/ChimericIdol.java b/Mage.Sets/src/mage/sets/prophecy/ChimericIdol.java index d5ec0ab43c3..166d82e9fb6 100644 --- a/Mage.Sets/src/mage/sets/prophecy/ChimericIdol.java +++ b/Mage.Sets/src/mage/sets/prophecy/ChimericIdol.java @@ -53,7 +53,7 @@ public class ChimericIdol extends CardImpl { this.expansionSetCode = "PCY"; // {0}: Tap all lands you control. Chimeric Idol becomes a 3/3 Turtle artifact creature until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapAllEffect(new FilterControlledLandPermanent("all lands you control")), new ManaCostsImpl("{0}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapAllEffect(new FilterControlledLandPermanent("lands you control")), new ManaCostsImpl("{0}")); ability.addEffect(new BecomesCreatureSourceEffect(new ChimericIdolToken(), "", Duration.EndOfTurn)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java b/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java index 03616f85f12..c31ead38fb5 100644 --- a/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java +++ b/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.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,19 +20,14 @@ * 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.worldwake; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -44,6 +39,10 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.permanent.token.Token; /** @@ -55,9 +54,13 @@ public class LavaclawReaches extends CardImpl { public LavaclawReaches(UUID ownerId) { super(ownerId, 139, "Lavaclaw Reaches", Rarity.RARE, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "WWK"; + + // Lavaclaw Reaches enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); + // {T}: Add {B} or {R} to your mana pool. this.addAbility(new BlackManaAbility()); this.addAbility(new RedManaAbility()); + // {1}{B}{R}: Until end of turn, Lavaclaw Reaches becomes a 2/2 black and red Elemental creature with ": This creature gets +X/+0 until end of turn." It's still a land. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new LavaclawReachesToken(), "land", Duration.EndOfTurn), new ManaCostsImpl("{1}{B}{R}"))); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java index e0056f8638c..834852f20fa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java @@ -64,4 +64,29 @@ public class TargetedTriggeredTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Kira, Great Glass-Spinner", 1); } + /** + * With Ashenmoor Liege on the battlefield, my opponent casts Claustrophobia + * on it without losing 4hp. + */ + @Test + public void testAshenmoorLiege() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Claustrophobia"); // {1}{U}{U} + + // Other black creatures you control get +1/+1. + // Other red creatures you control get +1/+1. + // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. + addCard(Zone.BATTLEFIELD, playerB, "Ashenmoor Liege", 1); // 4/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Claustrophobia", "Ashenmoor Liege"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 16); + + assertPermanentCount(playerA, "Claustrophobia", 1); + assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/AnafenzaTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/AnafenzaTest.java index 109b704982e..a1624958674 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/AnafenzaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/AnafenzaTest.java @@ -139,4 +139,37 @@ public class AnafenzaTest extends CardTestCommanderDuelBase { assertLife(playerB, 40); } + // Anafenza + Animated permanents + @Test + public void testAnafenzaExileAnimatedPermanents() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + // {0}: Tap all lands you control. Chimeric Idol becomes a 3/3 Turtle artifact creature until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Chimeric Idol"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. + // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Anafenza, the Foremost"); // 4/4 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{0}: Tap all lands you control"); + + attack(2, playerB, "Chimeric Idol"); + block(2, playerA, "Anafenza, the Foremost", "Chimeric Idol"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertExileCount("Chimeric Idol", 1); + assertGraveyardCount(playerB, "Chimeric Idol", 0); + assertPermanentCount(playerB, "Chimeric Idol", 0); + + assertTappedCount("Mountain", true, 3); + + } } From 69dc4f10ac1ff9df8b18ae9169762d1c1b0a3eeb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 23:03:03 +0200 Subject: [PATCH 22/39] * Fixed that spells with targets cast without paying mana could simply be canceled by canceling the target selection (e.g. player was able to cancel a spell cast by suspend what's not allowed by the rules). --- .../sets/gatecrash/IllusionistsBracers.java | 77 ++++++++-------- .../mage/sets/lorwyn/RingsOfBrighthearth.java | 22 ++--- .../sets/magic2014/StrionicResonator.java | 21 ++--- .../sets/magic2015/KurkeshOnakkeAncient.java | 2 +- .../counter/RemoveCounterSourceEffect.java | 28 +++--- Mage/src/mage/target/Targets.java | 87 ++++++++++--------- 6 files changed, 118 insertions(+), 119 deletions(-) diff --git a/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java b/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java index 7143298d447..ae59bc58e37 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java +++ b/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java @@ -1,30 +1,30 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.sets.gatecrash; import java.util.UUID; @@ -48,21 +48,21 @@ import mage.game.stack.StackAbility; import mage.players.Player; /** -* -* @author LevelX2 -*/ + * + * @author LevelX2 + */ public class IllusionistsBracers extends CardImpl { public IllusionistsBracers(UUID ownerId) { - super(ownerId, 231, "Illusionist's Bracers", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{2}"); - this.expansionSetCode = "GTC"; - this.subtype.add("Equipment"); + super(ownerId, 231, "Illusionist's Bracers", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.expansionSetCode = "GTC"; + this.subtype.add("Equipment"); - // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy. - this.addAbility(new AbilityActivatedTriggeredAbility()); + // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy. + this.addAbility(new AbilityActivatedTriggeredAbility()); - // Equip 3 - this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); + // Equip 3 + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3))); } public IllusionistsBracers(final IllusionistsBracers card) { @@ -76,6 +76,7 @@ public class IllusionistsBracers extends CardImpl { } class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl { + AbilityActivatedTriggeredAbility() { super(Zone.BATTLEFIELD, new CopyActivatedAbilityEffect()); } @@ -110,7 +111,7 @@ class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy."; + return "Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy."; } } @@ -142,7 +143,7 @@ class CopyActivatedAbilityEffect extends OneShotEffect { if (newAbility.getTargets().size() > 0) { if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, game) == false) { + if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { return false; } } diff --git a/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java b/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java index e591c15973e..4bf012e1bbb 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java +++ b/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java @@ -57,7 +57,7 @@ public class RingsOfBrighthearth extends CardImpl { this.expansionSetCode = "LRW"; // Whenever you activate an ability, if it isn't a mana ability, you may pay {2}. If you do, copy that ability. You may choose new targets for the copy. - this.addAbility(new RingsOfBrighthearthTriggeredAbility()); + this.addAbility(new RingsOfBrighthearthTriggeredAbility()); } public RingsOfBrighthearth(final RingsOfBrighthearth card) { @@ -71,15 +71,15 @@ public class RingsOfBrighthearth extends CardImpl { } class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { - + RingsOfBrighthearthTriggeredAbility() { super(Zone.BATTLEFIELD, new RingsOfBrighthearthEffect(), false); } - + RingsOfBrighthearthTriggeredAbility(final RingsOfBrighthearthTriggeredAbility ability) { super(ability); } - + @Override public RingsOfBrighthearthTriggeredAbility copy() { return new RingsOfBrighthearthTriggeredAbility(this); @@ -89,7 +89,7 @@ class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { public boolean checkEventType(GameEvent event, Game game) { return event.getType() == EventType.ACTIVATED_ABILITY; } - + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(getControllerId())) { @@ -102,7 +102,7 @@ class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { } return false; } - + @Override public String getRule() { return "Whenever you activate an ability, if it isn't a mana ability, you may pay {2}. If you do, copy that ability. You may choose new targets for the copy."; @@ -110,21 +110,21 @@ class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { } class RingsOfBrighthearthEffect extends OneShotEffect { - + RingsOfBrighthearthEffect() { super(Outcome.Benefit); this.staticText = ", you may pay {2}. If you do, copy that ability. You may choose new targets for the copy."; } - + RingsOfBrighthearthEffect(final RingsOfBrighthearthEffect effect) { super(effect); } - + @Override public RingsOfBrighthearthEffect copy() { return new RingsOfBrighthearthEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); @@ -142,7 +142,7 @@ class RingsOfBrighthearthEffect extends OneShotEffect { if (newAbility.getTargets().size() > 0) { if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, game) == false) { + if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { return false; } } diff --git a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java index c05126f8ba0..154ad1a4758 100644 --- a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java +++ b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java @@ -57,8 +57,6 @@ import mage.target.TargetObject; */ public class StrionicResonator extends CardImpl { - - public StrionicResonator(UUID ownerId) { super(ownerId, 224, "Strionic Resonator", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "M14"; @@ -92,8 +90,8 @@ class StrionicResonatorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - StackAbility stackAbility = (StackAbility)game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if(stackAbility != null){ + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); + if (stackAbility != null) { Ability ability = (Ability) stackAbility.getStackAbility(); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); @@ -104,7 +102,7 @@ class StrionicResonatorEffect extends OneShotEffect { if (newAbility.getTargets().size() > 0) { if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, game) == false) { + if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { return false; } } @@ -114,7 +112,7 @@ class StrionicResonatorEffect extends OneShotEffect { } } return false; - + } @Override @@ -122,7 +120,7 @@ class StrionicResonatorEffect extends OneShotEffect { return new StrionicResonatorEffect(this); } - @Override + @Override public String getText(Mode mode) { StringBuilder sb = new StringBuilder(); sb.append("Copy target ").append(mode.getTargets().get(0).getTargetName()).append(". You may choose new targets for the copy"); @@ -143,7 +141,6 @@ class TargetTriggeredAbility extends TargetObject { super(target); } - @Override public boolean canTarget(UUID id, Ability source, Game game) { if (source != null && source.getSourceId().equals(id)) { @@ -164,11 +161,11 @@ class TargetTriggeredAbility extends TargetObject { @Override public boolean canChoose(UUID sourceControllerId, Game game) { - for (StackObject stackObject : game.getStack()) { + for (StackObject stackObject : game.getStack()) { if (stackObject.getStackAbility() != null && stackObject.getStackAbility() instanceof TriggeredAbility && game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getStackAbility().getControllerId())) { - return true; - } + return true; } + } return false; } @@ -180,7 +177,7 @@ class TargetTriggeredAbility extends TargetObject { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet(); - for (StackObject stackObject : game.getStack()) { + for (StackObject stackObject : game.getStack()) { if (stackObject.getStackAbility() != null && stackObject.getStackAbility() instanceof TriggeredAbility && game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getStackAbility().getControllerId())) { possibleTargets.add(stackObject.getStackAbility().getId()); } diff --git a/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java b/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java index 961a2c742fe..ad94b5cce07 100644 --- a/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java +++ b/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java @@ -152,7 +152,7 @@ class KurkeshOnakkeAncientEffect extends OneShotEffect { if (newAbility.getTargets().size() > 0) { if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, game) == false) { + if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { return false; } } diff --git a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java index 6da3bf9cf00..b76c5771bc9 100644 --- a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.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 mage.abilities.effects.common.counter; import mage.abilities.Ability; @@ -40,8 +39,8 @@ import mage.game.permanent.Permanent; * * @author Loki */ - public class RemoveCounterSourceEffect extends OneShotEffect { + private final Counter counter; public RemoveCounterSourceEffect(Counter counter) { @@ -57,25 +56,24 @@ public class RemoveCounterSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null && p.getCounters().getCount(counter.getName()) >= counter.getCount()) { - p.removeCounters(counter.getName(), counter.getCount(), game); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.getCounters().getCount(counter.getName()) >= counter.getCount()) { + permanent.removeCounters(counter.getName(), counter.getCount(), game); if (!game.isSimulation()) { - game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(" ").append(counter.getName()) - .append(" counter from ").append(p.getName()).toString()); + game.informPlayers("Removed " + counter.getCount() + " " + counter.getName() + " counter from " + permanent.getLogName()); } return true; } - Card c = game.getCard(source.getSourceId()); - if (c != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) { - c.removeCounters(counter.getName(), counter.getCount(), game); + Card card = game.getCard(source.getSourceId()); + if (card != null && card.getCounters(game).getCount(counter.getName()) >= counter.getCount()) { + card.removeCounters(counter.getName(), counter.getCount(), game); if (!game.isSimulation()) { - game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(" ").append(counter.getName()) - .append(" counter from ").append(c.getName()) - .append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString()); + game.informPlayers("Removed " + counter.getCount() + " " + counter.getName() + + " counter from " + card.getLogName() + + " (" + card.getCounters(game).getCount(counter.getName()) + " left)"); } return true; - } + } return false; } @@ -84,7 +82,7 @@ public class RemoveCounterSourceEffect extends OneShotEffect { return new RemoveCounterSourceEffect(this); } - private void setText() { + private void setText() { if (counter.getCount() > 1) { StringBuilder sb = new StringBuilder(); sb.append("remove ").append(Integer.toString(counter.getCount())).append(" ").append(counter.getName()).append(" counters from {this}"); diff --git a/Mage/src/mage/target/Targets.java b/Mage/src/mage/target/Targets.java index ad1f1c37fb5..c29f1932d90 100644 --- a/Mage/src/mage/target/Targets.java +++ b/Mage/src/mage/target/Targets.java @@ -1,58 +1,57 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.target; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.constants.Outcome; import mage.abilities.Ability; +import mage.constants.Outcome; import mage.game.Game; - /** * * @author BetaSteward_at_googlemail.com */ public class Targets extends ArrayList { - public Targets() {} + public Targets() { + } public Targets(final Targets targets) { - for (Target target: targets) { + for (Target target : targets) { this.add(target.copy()); } } public List getUnchosen() { List unchosen = new ArrayList<>(); - for (Target target: this) { + for (Target target : this) { if (!target.isChosen()) { unchosen.add(target); } @@ -61,13 +60,13 @@ public class Targets extends ArrayList { } public void clearChosen() { - for (Target target: this) { + for (Target target : this) { target.clearChosen(); } } public boolean isChosen() { - for (Target target: this) { + for (Target target : this) { if (!target.isChosen()) { return false; } @@ -90,7 +89,7 @@ public class Targets extends ArrayList { return true; } - public boolean chooseTargets(Outcome outcome, UUID playerId, Ability source, Game game) { + public boolean chooseTargets(Outcome outcome, UUID playerId, Ability source, boolean noMana, Game game) { if (this.size() > 0) { if (!canChoose(source.getSourceId(), playerId, game)) { return false; @@ -101,6 +100,9 @@ public class Targets extends ArrayList { if (target.getTargetController() != null) { // some targets can have controller different than ability controller targetController = target.getTargetController(); } + if (noMana) { // if cast without mana (e.g. by supend you may notr be able to cancel the casting if you are able to cast it + target.setRequired(true); + } if (!target.chooseTarget(outcome, targetController, source, game)) { return false; } @@ -113,7 +115,7 @@ public class Targets extends ArrayList { // 608.2 // The spell or ability is countered if all its targets, for every instance of the word "target," are now illegal int illegalCount = 0; - for (Target target: this) { + for (Target target : this) { if (!target.isLegal(source, game)) { illegalCount++; } @@ -123,8 +125,8 @@ public class Targets extends ArrayList { } /** - * Checks if there are enough targets that can be chosen. Should only be used - * for Ability targets since this checks for protection, shroud etc. + * Checks if there are enough targets that can be chosen. Should only be + * used for Ability targets since this checks for protection, shroud etc. * * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source @@ -132,7 +134,7 @@ public class Targets extends ArrayList { * @return - true if enough valid targets exist */ public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - for (Target target: this) { + for (Target target : this) { if (!target.canChoose(sourceId, sourceControllerId, game)) { return false; } @@ -141,15 +143,16 @@ public class Targets extends ArrayList { } /** - * Checks if there are enough objects that can be selected. Should not be used - * for Ability targets since this does not check for protection, shroud etc. + * Checks if there are enough objects that can be selected. Should not be + * used for Ability targets since this does not check for protection, shroud + * etc. * * @param sourceControllerId - controller of the select event * @param game * @return - true if enough valid objects exist */ public boolean canChoose(UUID sourceControllerId, Game game) { - for (Target target: this) { + for (Target target : this) { if (!target.canChoose(sourceControllerId, game)) { return false; } From de639cc1d60b336f2a65d32f2180d71fc3f2d996 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Sep 2015 23:06:26 +0200 Subject: [PATCH 23/39] * Suspend, added the card name to the rule text of the ability triggering, so different suspended cards were not auto put on the stack by the activated same rule text option. --- .../src/mage/player/human/HumanPlayer.java | 12 +++++++----- Mage/src/mage/abilities/AbilityImpl.java | 2 +- Mage/src/mage/abilities/keyword/SuspendAbility.java | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 73fba0f4eae..882a68cfc34 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -383,7 +383,7 @@ public class HumanPlayer extends PlayerImpl { } while (!abort) { Set possibleTargets = target.possibleTargets(source == null ? null : source.getSourceId(), abilityControllerId, game); - boolean required = target.isRequired(source); + boolean required = target.isRequired(source != null ? source.getSourceId() : null, game); if (possibleTargets.isEmpty() || target.getTargets().size() >= target.getNumberOfTargets()) { required = false; } @@ -715,21 +715,23 @@ public class HumanPlayer extends PlayerImpl { if (triggerAutoOrderAbilityFirst.contains(ability.getOriginalId())) { return ability; } - if (triggerAutoOrderNameFirst.contains(ability.getRule())) { + MageObject object = game.getObject(ability.getSourceId()); + String rule = ability.getRule(object != null ? object.getName() : null); + if (triggerAutoOrderNameFirst.contains(rule)) { return ability; } if (triggerAutoOrderAbilityLast.contains(ability.getOriginalId())) { abilityOrderLast = ability; continue; } - if (triggerAutoOrderNameLast.contains(ability.getRule())) { + if (triggerAutoOrderNameLast.contains(rule)) { abilityOrderLast = ability; continue; } if (autoOrderUse) { if (autoOrderRuleText == null) { - autoOrderRuleText = ability.getRule(); - } else if (!ability.getRule().equals(autoOrderRuleText)) { + autoOrderRuleText = rule; + } else if (!rule.equals(autoOrderRuleText)) { autoOrderUse = false; } } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 7006eeebdc2..838204cfa09 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -335,7 +335,7 @@ public abstract class AbilityImpl implements Ability { if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in playerImpl.triggerAbility sourceObject.adjustTargets(this, game); } - if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) { + if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) { if ((variableManaCost != null || announceString != null) && !game.isSimulation()) { game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X"); } diff --git a/Mage/src/mage/abilities/keyword/SuspendAbility.java b/Mage/src/mage/abilities/keyword/SuspendAbility.java index 320f11979c4..fbb97482b8f 100644 --- a/Mage/src/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/mage/abilities/keyword/SuspendAbility.java @@ -328,7 +328,7 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "When the last time counter is removed from this card, if it's removed from the game, " + super.getRule(); + return "When the last time counter is removed from this card ({this}), if it's removed from the game, " + super.getRule(); } @Override @@ -436,7 +436,7 @@ class SuspendBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbili public SuspendBeginningOfUpkeepTriggeredAbility() { super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false), SuspendedCondition.getInstance(), - "At the beginning of your upkeep, if this card is suspended, remove a time counter from it."); + "At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it."); this.setRuleVisible(false); } From 05e7eedb65a3d1b77194c41d9dd67dcc63e070d8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 00:32:35 +0200 Subject: [PATCH 24/39] * Gwyllion Hedge-Mage - Fixed that the second triggered ability did not work. --- .../sets/commander/GwyllionHedgeMage.java | 27 ++++++++++--------- .../src/mage/sets/odyssey/MomentsPeace.java | 7 +++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander/GwyllionHedgeMage.java b/Mage.Sets/src/mage/sets/commander/GwyllionHedgeMage.java index 8e1840aac88..d596de1eae2 100644 --- a/Mage.Sets/src/mage/sets/commander/GwyllionHedgeMage.java +++ b/Mage.Sets/src/mage/sets/commander/GwyllionHedgeMage.java @@ -40,30 +40,28 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.permanent.token.KithkinToken; -import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; /** * * @author jeffwadsworth - + * */ public class GwyllionHedgeMage extends CardImpl { - + private static final FilterLandPermanent filter = new FilterLandPermanent("Plains"); private static final FilterLandPermanent filter2 = new FilterLandPermanent("Swamps"); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent(); static { filter.add(new SubtypePredicate("Plains")); - filter2.add(new SubtypePredicate("Swamps")); + filter2.add(new SubtypePredicate("Swamp")); } - - private String rule1 = "When {this} enters the battlefield, if you control two or more Plains, you may put a 1/1 white Kithkin Soldier creature token onto the battlefield."; - private String rule2 = "When {this} enters the battlefield, if you control two or more Swamps, you may put a -1/-1 counter on target creature."; + + private final String rule1 = "When {this} enters the battlefield, if you control two or more Plains, you may put a 1/1 white Kithkin Soldier creature token onto the battlefield."; + private final String rule2 = "When {this} enters the battlefield, if you control two or more Swamps, you may put a -1/-1 counter on target creature."; public GwyllionHedgeMage(UUID ownerId) { super(ownerId, 202, "Gwyllion Hedge-Mage", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{W/B}"); @@ -77,12 +75,15 @@ public class GwyllionHedgeMage extends CardImpl { // When Gwyllion Hedge-Mage enters the battlefield, if you control two or more Plains, you may put a 1/1 white Kithkin Soldier creature token onto the battlefield. Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KithkinToken()), true), new PermanentsOnTheBattlefieldCondition(filter, CountType.MORE_THAN, 1), rule1); this.addAbility(ability); - + // When Gwyllion Hedge-Mage enters the battlefield, if you control two or more Swamps, you may put a -1/-1 counter on target creature. - Ability ability2 = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true), new PermanentsOnTheBattlefieldCondition(filter2, CountType.MORE_THAN, 1), rule2); - ability2.addTarget(new TargetPermanent(filter3)); + Ability ability2 = new ConditionalTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true), + new PermanentsOnTheBattlefieldCondition(filter2, CountType.MORE_THAN, 1), + rule2); + ability2.addTarget(new TargetCreaturePermanent()); this.addAbility(ability2); - + } public GwyllionHedgeMage(final GwyllionHedgeMage card) { diff --git a/Mage.Sets/src/mage/sets/odyssey/MomentsPeace.java b/Mage.Sets/src/mage/sets/odyssey/MomentsPeace.java index 356c6690658..99d6411591d 100644 --- a/Mage.Sets/src/mage/sets/odyssey/MomentsPeace.java +++ b/Mage.Sets/src/mage/sets/odyssey/MomentsPeace.java @@ -28,14 +28,13 @@ package mage.sets.odyssey; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PreventAllDamageByAllEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.TimingRule; /** @@ -48,9 +47,9 @@ public class MomentsPeace extends CardImpl { super(ownerId, 251, "Moment's Peace", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); this.expansionSetCode = "ODY"; - // Prevent all combat damage that would be dealt this turn. this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn, true)); + // Flashback {2}{G} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{2}{G}"), TimingRule.INSTANT)); } From a6aa484ea0fa19bd87bb3d2245b7ab716324c0b6 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 11:31:57 +0300 Subject: [PATCH 25/39] Add description parameter to BecomesTappedAttachedTriggeredAbility. Use it and DestroyAttachedEffect for some existing cards. Fix several issues with Brink of Disaster. Implement card: Uncontrolled Infestation --- .../src/mage/sets/fifthedition/Blight.java | 55 +------------ Mage.Sets/src/mage/sets/iceage/Seizures.java | 2 +- .../src/mage/sets/planeshift/Insolence.java | 2 +- .../sets/riseoftheeldrazi/LustForWar.java | 2 +- .../sets/scourge/UncontrolledInfestation.java | 81 +++++++++++++++++++ .../mage/sets/urzassaga/SpreadingAlgae.java | 69 +++------------- Mage.Sets/src/mage/sets/visions/Betrayal.java | 2 +- .../mage/sets/worldwake/BrinkOfDisaster.java | 59 +++----------- ...BecomesTappedAttachedTriggeredAbility.java | 14 ++-- 9 files changed, 118 insertions(+), 168 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/scourge/UncontrolledInfestation.java diff --git a/Mage.Sets/src/mage/sets/fifthedition/Blight.java b/Mage.Sets/src/mage/sets/fifthedition/Blight.java index dc7821b2b6a..d3058c85ebc 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/Blight.java +++ b/Mage.Sets/src/mage/sets/fifthedition/Blight.java @@ -29,23 +29,16 @@ package mage.sets.fifthedition; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; import mage.abilities.keyword.EnchantAbility; 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.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -65,9 +58,9 @@ public class Blight extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // When enchanted land becomes tapped, destroy it. - this.addAbility(new BlightTriggeredAbility()); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedEffect("it"), "enchanted land")); } public Blight(final Blight card) { @@ -79,43 +72,3 @@ public class Blight extends CardImpl { return new Blight(this); } } - -class BlightTriggeredAbility extends TriggeredAbilityImpl { - BlightTriggeredAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect()); - } - - BlightTriggeredAbility(final BlightTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo().equals(event.getTargetId())) { - Permanent attached = game.getPermanent(enchantment.getAttachedTo()); - if (attached != null) { - for (Effect e : getEffects()) { - e.setTargetPointer(new FixedTarget(attached.getId())); - } - return true; - } - } - return false; - } - - @Override - public BlightTriggeredAbility copy() { - return new BlightTriggeredAbility(this); - } - - @Override - public String getRule() { - return "When enchanted land becomes tapped, destroy it."; - } -} diff --git a/Mage.Sets/src/mage/sets/iceage/Seizures.java b/Mage.Sets/src/mage/sets/iceage/Seizures.java index 5fdc2881cc2..df24823b79e 100644 --- a/Mage.Sets/src/mage/sets/iceage/Seizures.java +++ b/Mage.Sets/src/mage/sets/iceage/Seizures.java @@ -63,7 +63,7 @@ public class Seizures extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // Whenever enchanted creature becomes tapped, Seizures deals 3 damage to that creature's controller unless that player pays {3}. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new SeizuresEffect(), false)); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new SeizuresEffect(), "enchanted creature")); } public Seizures(final Seizures card) { diff --git a/Mage.Sets/src/mage/sets/planeshift/Insolence.java b/Mage.Sets/src/mage/sets/planeshift/Insolence.java index 4fdcbf1b6c5..9d8de7ca711 100644 --- a/Mage.Sets/src/mage/sets/planeshift/Insolence.java +++ b/Mage.Sets/src/mage/sets/planeshift/Insolence.java @@ -60,7 +60,7 @@ public class Insolence extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // Whenever enchanted creature becomes tapped, Insolence deals 2 damage to that creature's controller. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DamageAttachedControllerEffect(2), false)); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DamageAttachedControllerEffect(2), "enchanted creature")); } public Insolence(final Insolence card) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/LustForWar.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/LustForWar.java index 0f72528f309..9526e526681 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/LustForWar.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/LustForWar.java @@ -64,7 +64,7 @@ public class LustForWar extends CardImpl { this.addAbility(ability); // Whenever enchanted creature becomes tapped, Lust for War deals 3 damage to that creature's controller. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DamageAttachedControllerEffect(3), false)); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DamageAttachedControllerEffect(3), "enchanted creature")); // Enchanted creature attacks each turn if able. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/sets/scourge/UncontrolledInfestation.java b/Mage.Sets/src/mage/sets/scourge/UncontrolledInfestation.java new file mode 100644 index 00000000000..6f51b0716af --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/UncontrolledInfestation.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.scourge; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author LoneFox + */ +public class UncontrolledInfestation extends CardImpl { + + private static final FilterLandPermanent filter = new FilterLandPermanent("nonbasic land"); + + static{ + filter.add(Predicates.not(new SupertypePredicate("Basic"))); + } + + public UncontrolledInfestation(UUID ownerId) { + super(ownerId, 108, "Uncontrolled Infestation", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Aura"); + + // Enchant nonbasic land + TargetPermanent auraTarget = new TargetLandPermanent(filter); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // When enchanted land becomes tapped, destroy it. + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedEffect("it"), "enchanted land")); + } + + public UncontrolledInfestation(final UncontrolledInfestation card) { + super(card); + } + + @Override + public UncontrolledInfestation copy() { + return new UncontrolledInfestation(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/SpreadingAlgae.java b/Mage.Sets/src/mage/sets/urzassaga/SpreadingAlgae.java index 3f944b98a29..117c5bca447 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/SpreadingAlgae.java +++ b/Mage.Sets/src/mage/sets/urzassaga/SpreadingAlgae.java @@ -29,11 +29,10 @@ package mage.sets.urzassaga; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -41,15 +40,9 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -57,29 +50,29 @@ import mage.target.targetpointer.FixedTarget; */ public class SpreadingAlgae extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("Swamp"); + private static final FilterPermanent filter = new FilterPermanent("Swamp"); + static{ filter.add(new SubtypePredicate("Swamp")); } + public SpreadingAlgae(UUID ownerId) { super(ownerId, 274, "Spreading Algae", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.expansionSetCode = "USG"; this.subtype.add("Aura"); - // Enchant Swamp - - TargetPermanent auraTarget = new TargetLandPermanent(); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // When enchanted land becomes tapped, destroy it. - this.addAbility(new SpreadingAlgaeTriggeredAbility(new DestroyTargetEffect())); - + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedEffect("it"), "enchanted land")); + // When Spreading Algae is put into a graveyard from the battlefield, return Spreading Algae to its owner's hand. this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new ReturnToHandSourceEffect())); - + } public SpreadingAlgae(final SpreadingAlgae card) { @@ -91,43 +84,3 @@ public class SpreadingAlgae extends CardImpl { return new SpreadingAlgae(this); } } - - -class SpreadingAlgaeTriggeredAbility extends TriggeredAbilityImpl { - - public SpreadingAlgaeTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - public SpreadingAlgaeTriggeredAbility(final SpreadingAlgaeTriggeredAbility ability) { - super(ability); - } - - @Override - public SpreadingAlgaeTriggeredAbility copy() { - return new SpreadingAlgaeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchant = game.getPermanent(sourceId); - if (enchant != null && enchant.getAttachedTo() != null) { - if (event.getTargetId().equals(enchant.getAttachedTo())) { - getEffects().get(0).setTargetPointer(new FixedTarget(enchant.getAttachedTo())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When enchanted permanent becomes tapped, destroy it."; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/visions/Betrayal.java b/Mage.Sets/src/mage/sets/visions/Betrayal.java index db86ed389fc..94658d95827 100644 --- a/Mage.Sets/src/mage/sets/visions/Betrayal.java +++ b/Mage.Sets/src/mage/sets/visions/Betrayal.java @@ -67,7 +67,7 @@ public class Betrayal extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // Whenever enchanted creature becomes tapped, you draw a card. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DrawCardSourceControllerEffect(1), "enchanted creature")); } public Betrayal(final Betrayal card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/BrinkOfDisaster.java b/Mage.Sets/src/mage/sets/worldwake/BrinkOfDisaster.java index bb646a302af..c0222a26374 100644 --- a/Mage.Sets/src/mage/sets/worldwake/BrinkOfDisaster.java +++ b/Mage.Sets/src/mage/sets/worldwake/BrinkOfDisaster.java @@ -28,24 +28,19 @@ package mage.sets.worldwake; import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -53,7 +48,7 @@ import mage.target.targetpointer.FixedTarget; */ public class BrinkOfDisaster extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterPermanent filter = new FilterPermanent("creature or land"); static { filter.add(Predicates.or( @@ -68,12 +63,14 @@ public class BrinkOfDisaster extends CardImpl { // Enchant creature or land - TargetPermanent auraTarget = new TargetPermanent(); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); // When enchanted permanent becomes tapped, destroy it. - this.addAbility(new EnchantedBecomesTappedTriggeredAbility(new DestroyTargetEffect())); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedEffect("it"), "enchanted permanent")); } public BrinkOfDisaster(final BrinkOfDisaster card) { @@ -85,41 +82,3 @@ public class BrinkOfDisaster extends CardImpl { return new BrinkOfDisaster(this); } } - -class EnchantedBecomesTappedTriggeredAbility extends TriggeredAbilityImpl { - - public EnchantedBecomesTappedTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - public EnchantedBecomesTappedTriggeredAbility(final EnchantedBecomesTappedTriggeredAbility ability) { - super(ability); - } - - @Override - public EnchantedBecomesTappedTriggeredAbility copy() { - return new EnchantedBecomesTappedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchant = game.getPermanent(sourceId); - if (enchant != null && enchant.getAttachedTo() != null) { - if (event.getTargetId().equals(enchant.getAttachedTo())) { - getEffects().get(0).setTargetPointer(new FixedTarget(enchant.getAttachedTo())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When enchanted permanent becomes tapped, destroy it."; - } -} diff --git a/Mage/src/mage/abilities/common/BecomesTappedAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/BecomesTappedAttachedTriggeredAbility.java index 3c09bae181c..2b8ef1b3426 100644 --- a/Mage/src/mage/abilities/common/BecomesTappedAttachedTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/BecomesTappedAttachedTriggeredAbility.java @@ -41,16 +41,20 @@ import mage.game.permanent.Permanent; */ public class BecomesTappedAttachedTriggeredAbility extends TriggeredAbilityImpl { - public BecomesTappedAttachedTriggeredAbility(Effect effect, boolean isOptional) { - super(Zone.BATTLEFIELD, effect, isOptional); + private final String description; + + public BecomesTappedAttachedTriggeredAbility(Effect effect, String description) { + this(effect, description, false); } - public BecomesTappedAttachedTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); + public BecomesTappedAttachedTriggeredAbility(Effect effect, String description, boolean isOptional) { + super(Zone.BATTLEFIELD, effect, isOptional); + this.description = description; } public BecomesTappedAttachedTriggeredAbility(final BecomesTappedAttachedTriggeredAbility ability) { super(ability); + this.description = ability.description; } @Override @@ -75,6 +79,6 @@ public class BecomesTappedAttachedTriggeredAbility extends TriggeredAbilityImpl @Override public String getRule() { - return "Whenever enchanted creature becomes tapped, " + super.getRule(); + return "Whenever " + description + " becomes tapped, " + super.getRule(); } } From 41ca5f198f6b0a06befa5ebf1330b63df8fe9439 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 12:31:08 +0300 Subject: [PATCH 26/39] Add LoseLifeControllerAttachedEffect. Kill some custom abilities and effects. Implement card: Pooling Venom --- .../src/mage/sets/conflux/CorruptedRoots.java | 63 ++---------- .../sets/dragonsmaze/SinisterPossession.java | 64 +----------- .../mage/sets/futuresight/PoolingVenom.java | 78 +++++++++++++++ .../mage/sets/limitedalpha/PsychicVenom.java | 53 +--------- .../sets/ninthedition/ContaminatedBond.java | 54 +--------- .../riseoftheeldrazi/ContaminatedGround.java | 55 +---------- .../LoseLifeControllerAttachedEffect.java | 98 +++++++++++++++++++ 7 files changed, 193 insertions(+), 272 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/futuresight/PoolingVenom.java create mode 100644 Mage/src/mage/abilities/effects/common/LoseLifeControllerAttachedEffect.java diff --git a/Mage.Sets/src/mage/sets/conflux/CorruptedRoots.java b/Mage.Sets/src/mage/sets/conflux/CorruptedRoots.java index 71acd4c156a..cec2b4e7def 100644 --- a/Mage.Sets/src/mage/sets/conflux/CorruptedRoots.java +++ b/Mage.Sets/src/mage/sets/conflux/CorruptedRoots.java @@ -29,26 +29,18 @@ package mage.sets.conflux; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -56,7 +48,7 @@ import mage.target.targetpointer.FixedTarget; */ public class CorruptedRoots extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("Forest or Plains"); + private static final FilterPermanent filter = new FilterPermanent("Forest or Plains"); static { filter.add(Predicates.or( @@ -69,17 +61,15 @@ public class CorruptedRoots extends CardImpl { this.expansionSetCode = "CON"; this.subtype.add("Aura"); - // Enchant Forest or Plains - TargetPermanent auraTarget = new TargetLandPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // Whenever enchanted land becomes tapped, its controller loses 2 life. - this.addAbility(new CorruptedRootsTriggeredAbility()); - + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new LoseLifeControllerAttachedEffect(2), "enchanted land")); } public CorruptedRoots(final CorruptedRoots card) { @@ -91,44 +81,3 @@ public class CorruptedRoots extends CardImpl { return new CorruptedRoots(this); } } - -class CorruptedRootsTriggeredAbility extends TriggeredAbilityImpl { - - CorruptedRootsTriggeredAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(2)); - } - - CorruptedRootsTriggeredAbility(final CorruptedRootsTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(this.sourceId); - if (enchantment != null && enchantment.getAttachedTo().equals(event.getTargetId())) { - Permanent attached = game.getPermanent(enchantment.getAttachedTo()); - if (attached != null) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(attached.getControllerId())); - } - return true; - } - } - return false; - } - - @Override - public CorruptedRootsTriggeredAbility copy() { - return new CorruptedRootsTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Whenever enchanted land becomes tapped, its controller loses 2 life."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/SinisterPossession.java b/Mage.Sets/src/mage/sets/dragonsmaze/SinisterPossession.java index a06d7f1042b..961d95af885 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/SinisterPossession.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/SinisterPossession.java @@ -29,19 +29,15 @@ package mage.sets.dragonsmaze; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; import mage.abilities.keyword.EnchantAbility; 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; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -81,61 +77,3 @@ public class SinisterPossession extends CardImpl { return new SinisterPossession(this); } } - -class LoseLifeControllerAttachedEffect extends OneShotEffect { - - protected DynamicValue amount; - - public LoseLifeControllerAttachedEffect(int amount) { - this(new StaticValue(amount)); - } - - public LoseLifeControllerAttachedEffect(DynamicValue amount) { - super(Outcome.Damage); - this.amount = amount; - setText(); - } - - public LoseLifeControllerAttachedEffect(final LoseLifeControllerAttachedEffect effect) { - super(effect); - this.amount = effect.amount.copy(); - } - - @Override - public LoseLifeControllerAttachedEffect copy() { - return new LoseLifeControllerAttachedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment == null) { - enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature == null) { - creature = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (creature != null) { - Player player = game.getPlayer(creature.getControllerId()); - if (player != null) { - player.loseLife(amount.calculate(game, source, this), game); - return true; - } - } - } - return false; - } - - private void setText() { - StringBuilder sb = new StringBuilder(); - sb.append("it's controller loses ").append(amount.toString()).append(" life"); - String message = amount.getMessage(); - if (message.length() > 0) { - sb.append(" for each "); - sb.append(message); - } - staticText = sb.toString(); - } -} diff --git a/Mage.Sets/src/mage/sets/futuresight/PoolingVenom.java b/Mage.Sets/src/mage/sets/futuresight/PoolingVenom.java new file mode 100644 index 00000000000..d383a8aefea --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/PoolingVenom.java @@ -0,0 +1,78 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author LoneFox + */ +public class PoolingVenom extends CardImpl { + + public PoolingVenom(UUID ownerId) { + super(ownerId, 74, "Pooling Venom", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Aura"); + + // Enchant land + TargetPermanent auraTarget = new TargetLandPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Whenever enchanted land becomes tapped, its controller loses 2 life. + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new LoseLifeControllerAttachedEffect(2), "enchanted land")); + // {3}{B}: Destroy enchanted land. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyAttachedEffect("enchanted land"), new ManaCostsImpl("{3}{B}"))); + } + + public PoolingVenom(final PoolingVenom card) { + super(card); + } + + @Override + public PoolingVenom copy() { + return new PoolingVenom(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/PsychicVenom.java b/Mage.Sets/src/mage/sets/limitedalpha/PsychicVenom.java index 20f2a5d9b91..d0322dc1c74 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/PsychicVenom.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/PsychicVenom.java @@ -30,22 +30,17 @@ package mage.sets.limitedalpha; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DamageAttachedControllerEffect; import mage.abilities.keyword.EnchantAbility; 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.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -67,7 +62,9 @@ public class PsychicVenom extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // Whenever enchanted land becomes tapped, Psychic Venom deals 2 damage to that land's controller. - this.addAbility(new PsychicVenomAbility()); + Effect effect = new DamageAttachedControllerEffect(2); + effect.setText("{this} deals 2 damage to that land's controller"); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(effect, "enchanted land")); } public PsychicVenom(final PsychicVenom card) { @@ -79,43 +76,3 @@ public class PsychicVenom extends CardImpl { return new PsychicVenom(this); } } - -class PsychicVenomAbility extends TriggeredAbilityImpl { - PsychicVenomAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(2, true, "that land's controller")); - } - - PsychicVenomAbility(final PsychicVenomAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent source = game.getPermanent(this.sourceId); - if (source != null && source.getAttachedTo().equals(event.getTargetId())) { - Permanent attached = game.getPermanent(source.getAttachedTo()); - if (attached != null) { - for (Effect e : getEffects()) { - e.setTargetPointer(new FixedTarget(attached.getControllerId())); - } - return true; - } - } - return false; - } - - @Override - public PsychicVenomAbility copy() { - return new PsychicVenomAbility(this); - } - - @Override - public String getRule() { - return "Whenever enchanted land becomes tapped, " + super.getRule(); - } -} diff --git a/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java b/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java index c2ec93bd388..7f03c8fb9e3 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java +++ b/Mage.Sets/src/mage/sets/ninthedition/ContaminatedBond.java @@ -30,19 +30,14 @@ package mage.sets.ninthedition; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; import mage.abilities.keyword.EnchantAbility; 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; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -77,50 +72,3 @@ public class ContaminatedBond extends CardImpl { return new ContaminatedBond(this); } } - -class LoseLifeControllerAttachedEffect extends OneShotEffect { - - protected DynamicValue amount; - - public LoseLifeControllerAttachedEffect(int amount) { - this(new StaticValue(amount)); - } - - public LoseLifeControllerAttachedEffect(DynamicValue amount) { - super(Outcome.Damage); - this.amount = amount; - staticText = "its controller loses " + amount.toString() +" life"; - } - - public LoseLifeControllerAttachedEffect(final LoseLifeControllerAttachedEffect effect) { - super(effect); - this.amount = effect.amount.copy(); - } - - @Override - public LoseLifeControllerAttachedEffect copy() { - return new LoseLifeControllerAttachedEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment == null) { - enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature == null) { - creature = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (creature != null) { - Player player = game.getPlayer(creature.getControllerId()); - if (player != null) { - player.loseLife(amount.calculate(game, source, this), game); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java index 98dbcb72f86..d7b41894cb8 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ContaminatedGround.java @@ -29,11 +29,10 @@ package mage.sets.riseoftheeldrazi; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; import mage.abilities.effects.common.continuous.BecomesBasicLandEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -41,13 +40,8 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import mage.target.targetpointer.FixedTarget; /** * @author Loki @@ -69,9 +63,9 @@ public class ContaminatedGround extends CardImpl { // Enchanted land is a Swamp. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesBasicLandEnchantedEffect("Swamp"))); - + // Whenever enchanted land becomes tapped, its controller loses 2 life. - this.addAbility(new ContaminatedGroundAbility()); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new LoseLifeControllerAttachedEffect(2), "enchanted land")); } public ContaminatedGround(final ContaminatedGround card) { @@ -83,44 +77,3 @@ public class ContaminatedGround extends CardImpl { return new ContaminatedGround(this); } } - -class ContaminatedGroundAbility extends TriggeredAbilityImpl { - ContaminatedGroundAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(2)); - } - - ContaminatedGroundAbility(final ContaminatedGroundAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent source = game.getPermanent(this.sourceId); - if (source != null && source.getAttachedTo().equals(event.getTargetId())) { - Permanent attached = game.getPermanent(source.getAttachedTo()); - if (attached != null) { - - for (Effect e : getEffects()) { - e.setTargetPointer(new FixedTarget(attached.getControllerId())); - } - return true; - } - } - return false; - } - - @Override - public ContaminatedGroundAbility copy() { - return new ContaminatedGroundAbility(this); - } - - @Override - public String getRule() { - return "Whenever enchanted land becomes tapped, its controller loses 2 life."; - } -} diff --git a/Mage/src/mage/abilities/effects/common/LoseLifeControllerAttachedEffect.java b/Mage/src/mage/abilities/effects/common/LoseLifeControllerAttachedEffect.java new file mode 100644 index 00000000000..fb0f482d2e6 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/LoseLifeControllerAttachedEffect.java @@ -0,0 +1,98 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + + +public class LoseLifeControllerAttachedEffect extends OneShotEffect { + + protected DynamicValue amount; + + public LoseLifeControllerAttachedEffect(int amount) { + this(new StaticValue(amount)); + } + + public LoseLifeControllerAttachedEffect(DynamicValue amount) { + super(Outcome.LoseLife); + this.amount = amount; + setText(); + } + + public LoseLifeControllerAttachedEffect(final LoseLifeControllerAttachedEffect effect) { + super(effect); + this.amount = effect.amount.copy(); + } + + @Override + public LoseLifeControllerAttachedEffect copy() { + return new LoseLifeControllerAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment == null) { + enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + } + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature == null) { + creature = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + } + if (creature != null) { + Player player = game.getPlayer(creature.getControllerId()); + if (player != null) { + player.loseLife(amount.calculate(game, source, this), game); + return true; + } + } + } + return false; + } + + private void setText() { + StringBuilder sb = new StringBuilder(); + sb.append("it's controller loses ").append(amount.toString()).append(" life"); + String message = amount.getMessage(); + if (message.length() > 0) { + sb.append(" for each "); + sb.append(message); + } + staticText = sb.toString(); + } +} From dcabc4692488b8cb638ea5aa58d55c5cb5bf129f Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 14:24:49 +0300 Subject: [PATCH 27/39] Implement cards: Aven Farseer, Fatal Mutation, Root Elemental, and Woodcloaker --- .../src/mage/sets/scourge/AvenFarseer.java | 69 +++++++++++ .../src/mage/sets/scourge/FatalMutation.java | 115 ++++++++++++++++++ .../src/mage/sets/scourge/RootElemental.java | 68 +++++++++++ .../src/mage/sets/scourge/Woodcloaker.java | 73 +++++++++++ 4 files changed, 325 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scourge/AvenFarseer.java create mode 100644 Mage.Sets/src/mage/sets/scourge/FatalMutation.java create mode 100644 Mage.Sets/src/mage/sets/scourge/RootElemental.java create mode 100644 Mage.Sets/src/mage/sets/scourge/Woodcloaker.java diff --git a/Mage.Sets/src/mage/sets/scourge/AvenFarseer.java b/Mage.Sets/src/mage/sets/scourge/AvenFarseer.java new file mode 100644 index 00000000000..b4c80304688 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/AvenFarseer.java @@ -0,0 +1,69 @@ +/* + * 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.scourge; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.TurnedFaceUpAllTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; + +/** + * + * @author LoneFox + */ +public class AvenFarseer extends CardImpl { + + public AvenFarseer(UUID ownerId) { + super(ownerId, 3, "Aven Farseer", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Bird"); + this.subtype.add("Soldier"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Whenever a permanent is turned face up, put a +1/+1 counter on Aven Farseer. + this.addAbility(new TurnedFaceUpAllTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new FilterPermanent("a permanent"))); + } + + public AvenFarseer(final AvenFarseer card) { + super(card); + } + + @Override + public AvenFarseer copy() { + return new AvenFarseer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/FatalMutation.java b/Mage.Sets/src/mage/sets/scourge/FatalMutation.java new file mode 100644 index 00000000000..65205011891 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/FatalMutation.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.scourge; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DestroyAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +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.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class FatalMutation extends CardImpl { + + public FatalMutation(UUID ownerId) { + super(ownerId, 66, "Fatal Mutation", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // When enchanted creature is turned face up, destroy it. It can't be regenerated. + this.addAbility(new FatalMutationAbility(new DestroyAttachedEffect("it", true))); + } + + public FatalMutation(final FatalMutation card) { + super(card); + } + + @Override + public FatalMutation copy() { + return new FatalMutation(this); + } +} + +class FatalMutationAbility extends TriggeredAbilityImpl { + + public FatalMutationAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect, false); + } + + public FatalMutationAbility(final FatalMutationAbility ability) { + super(ability); + } + + @Override + public FatalMutationAbility copy() { + return new FatalMutationAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TURNEDFACEUP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent attachment = game.getPermanent(this.getSourceId()); + if(attachment != null && event.getTargetId().equals(attachment.getAttachedTo())) { + return true; + } + return false; + + } + + @Override + public String getRule() { + return "Whenever enchanted creature is turned face up, " + super.getRule(); + } + +} + diff --git a/Mage.Sets/src/mage/sets/scourge/RootElemental.java b/Mage.Sets/src/mage/sets/scourge/RootElemental.java new file mode 100644 index 00000000000..46d93c2732c --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/RootElemental.java @@ -0,0 +1,68 @@ +/* + * 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.scourge; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PutPermanentOnBattlefieldEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; + +/** + * + * @author LoneFox + */ +public class RootElemental extends CardImpl { + + public RootElemental(UUID ownerId) { + super(ownerId, 127, "Root Elemental", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Elemental"); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // Morph {5}{G}{G} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{5}{G}{G}"))); + // When Root Elemental is turned face up, you may put a creature card from your hand onto the battlefield. + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new PutPermanentOnBattlefieldEffect(new FilterCreatureCard("a creature card")))); + } + + public RootElemental(final RootElemental card) { + super(card); + } + + @Override + public RootElemental copy() { + return new RootElemental(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/Woodcloaker.java b/Mage.Sets/src/mage/sets/scourge/Woodcloaker.java new file mode 100644 index 00000000000..0fc2a523f84 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/Woodcloaker.java @@ -0,0 +1,73 @@ +/* + * 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.scourge; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.MorphAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; + +/** + * + * @author LoneFox + */ +public class Woodcloaker extends CardImpl { + + public Woodcloaker(UUID ownerId) { + super(ownerId, 134, "Woodcloaker", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{5}{G}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Elf"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Morph {2}{G}{G} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{2}{G}{G}"))); + // When Woodcloaker is turned face up, target creature gains trample until end of turn. + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public Woodcloaker(final Woodcloaker card) { + super(card); + } + + @Override + public Woodcloaker copy() { + return new Woodcloaker(this); + } +} From d2a97b0b645f70330e6447d893398bdf20bc7aee Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 15:47:50 +0300 Subject: [PATCH 28/39] Implement cards: Cloudseeder, Llanowar Mentor, Skirk Ridge Exhumer, and Sparkspitter --- .../mage/sets/futuresight/Cloudseeder.java | 94 ++++++++++++++++++ .../mage/sets/futuresight/LlanowarMentor.java | 90 +++++++++++++++++ .../sets/futuresight/SkirkRidgeExhumer.java | 96 +++++++++++++++++++ .../mage/sets/futuresight/Sparkspitter.java | 95 ++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/futuresight/Cloudseeder.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/LlanowarMentor.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/SkirkRidgeExhumer.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/Sparkspitter.java diff --git a/Mage.Sets/src/mage/sets/futuresight/Cloudseeder.java b/Mage.Sets/src/mage/sets/futuresight/Cloudseeder.java new file mode 100644 index 00000000000..7a6bbad56b6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/Cloudseeder.java @@ -0,0 +1,94 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CanBlockOnlyFlyingAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.permanent.token.Token; + +/** + * + * @author LoneFox + */ +public class Cloudseeder extends CardImpl { + + public Cloudseeder(UUID ownerId) { + super(ownerId, 33, "Cloudseeder", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Faerie"); + this.subtype.add("Spellshaper"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // {U}, {tap}, Discard a card: Put a 1/1 blue Faerie creature token named Cloud Sprite onto the battlefield. It has flying and "Cloud Sprite can block only creatures with flying." + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new CloudSpriteToken()), new ManaCostsImpl("{U}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public Cloudseeder(final Cloudseeder card) { + super(card); + } + + @Override + public Cloudseeder copy() { + return new Cloudseeder(this); + } +} + +class CloudSpriteToken extends Token { + + public CloudSpriteToken() { + super("Cloud Sprite", "1/1 blue faerie creature token named Cloud Sprite with flying and \"Cloud Sprite can block only creatures with flying.\""); + this.setOriginalExpansionSetCode("FUT"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add("Faerie"); + power = new MageInt(1); + toughness = new MageInt(1); + + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(new CanBlockOnlyFlyingAbility()); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/LlanowarMentor.java b/Mage.Sets/src/mage/sets/futuresight/LlanowarMentor.java new file mode 100644 index 00000000000..c4a4c9e04b8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/LlanowarMentor.java @@ -0,0 +1,90 @@ +/* + * 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.futuresight; + +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.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.permanent.token.Token; + +/** + * + * @author LoneFox + */ +public class LlanowarMentor extends CardImpl { + + public LlanowarMentor(UUID ownerId) { + super(ownerId, 131, "Llanowar Mentor", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{G}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Elf"); + this.subtype.add("Spellshaper"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {G}, {tap}, Discard a card: Put a 1/1 green Elf Druid creature token named Llanowar Elves onto the battlefield. It has "{tap}: Add {G} to your mana pool." + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new LlanowarElvesToken()), new ManaCostsImpl("{G}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public LlanowarMentor(final LlanowarMentor card) { + super(card); + } + + @Override + public LlanowarMentor copy() { + return new LlanowarMentor(this); + } +} + +class LlanowarElvesToken extends Token { + + public LlanowarElvesToken() { + super("Llanowar Elves", "1/1 green Elf Druid creature token named Llanowar Elves with \"{T}: Add {G} to your mana pool.\""); + this.setOriginalExpansionSetCode("FUT"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add("Elf"); + subtype.add("Druid"); + power = new MageInt(1); + toughness = new MageInt(1); + + this.addAbility(new GreenManaAbility()); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/SkirkRidgeExhumer.java b/Mage.Sets/src/mage/sets/futuresight/SkirkRidgeExhumer.java new file mode 100644 index 00000000000..7338981ff76 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/SkirkRidgeExhumer.java @@ -0,0 +1,96 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.permanent.token.Token; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class SkirkRidgeExhumer extends CardImpl { + + public SkirkRidgeExhumer(UUID ownerId) { + super(ownerId, 77, "Skirk Ridge Exhumer", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Zombie"); + this.subtype.add("Spellshaper"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {B}, {tap}, Discard a card: Put a 1/1 black Zombie Goblin creature token named Festering Goblin onto the battlefield. It has "When Festering Goblin dies, target creature gets -1/-1 until end of turn." + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new FesteringGoblinToken()), new ManaCostsImpl("{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public SkirkRidgeExhumer(final SkirkRidgeExhumer card) { + super(card); + } + + @Override + public SkirkRidgeExhumer copy() { + return new SkirkRidgeExhumer(this); + } +} + +class FesteringGoblinToken extends Token { + + public FesteringGoblinToken() { + super("Festering Goblin", "1/1 black Zombie Goblin creature token named Festering Goblin with \"When Festering Goblin dies, target creature gets -1/-1 until end of turn.\""); + this.setOriginalExpansionSetCode("FUT"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add("Zombie"); + subtype.add("Goblin"); + power = new MageInt(1); + toughness = new MageInt(1); + + Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/Sparkspitter.java b/Mage.Sets/src/mage/sets/futuresight/Sparkspitter.java new file mode 100644 index 00000000000..83228c4f17c --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/Sparkspitter.java @@ -0,0 +1,95 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.permanent.token.Token; + +/** + * + * @author LoneFox + */ +public class Sparkspitter extends CardImpl { + + public Sparkspitter(UUID ownerId) { + super(ownerId, 109, "Sparkspitter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Elemental"); + this.subtype.add("Spellshaper"); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {R}, {tap}, Discard a card: Put a 3/1 red Elemental creature token named Spark Elemental onto the battlefield. It has trample, haste, and "At the beginning of the end step, sacrifice Spark Elemental." + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SparkElementalToken()), new ManaCostsImpl("{R}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public Sparkspitter(final Sparkspitter card) { + super(card); + } + + @Override + public Sparkspitter copy() { + return new Sparkspitter(this); + } +} + +class SparkElementalToken extends Token { + + public SparkElementalToken() { + super("Spark Elemental", "3/1 red Elemental creature token named Spark Elemental with trample, haste, and \"At the beginning of the end step, sacrifice Spark Elemental.\""); + this.setOriginalExpansionSetCode("FUT"); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add("Elemental"); + power = new MageInt(3); + toughness = new MageInt(1); + + this.addAbility(TrampleAbility.getInstance()); + this.addAbility(HasteAbility.getInstance()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, false)); + } +} From 1175a186613c7789cd45ea7116655181657cdc12 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 14:52:14 +0200 Subject: [PATCH 29/39] * Fixed put token into play thta's of copy of target effects to handle targets correctly that already copy something (e.g. Kiki-Jiki, Mirror Breaker copying a Body Double creature). --- .../KikiJikiMirrorBreaker.java | 34 ++--- .../sets/commander/RikuOfTwoReflections.java | 47 ++----- .../src/mage/sets/commander/Spawnwrithe.java | 4 +- .../commander2014/FeldonOfTheThirdPath.java | 21 +-- .../sets/conflux/MirrorSigilSergeant.java | 55 +------- .../src/mage/sets/darkascension/Seance.java | 36 ++--- .../sets/dissension/SproutingPhytohydra.java | 7 +- .../sets/dragonsmaze/ProgenitorMimic.java | 66 +-------- .../sets/dragonsoftarkir/CloneLegion.java | 10 +- .../sets/dragonsoftarkir/MirrorMockery.java | 18 +-- .../src/mage/sets/eventide/SpittingImage.java | 9 +- .../sets/fatereforged/FlamerushRider.java | 20 ++- .../mage/sets/fatereforged/SupplantForm.java | 42 +----- .../mage/sets/gatecrash/GiantAdephage.java | 54 +------ .../mage/sets/gatecrash/StolenIdentity.java | 59 +------- .../mage/sets/innistrad/BackFromTheBrink.java | 66 +++------ .../mage/sets/journeyintonyx/Twinflame.java | 46 +++--- .../src/mage/sets/mirrodin/SoulFoundry.java | 9 +- .../sets/mirrodinbesieged/Mirrorworks.java | 133 ++++-------------- .../src/mage/sets/planarchaos/Chronozoa.java | 22 +-- .../mage/sets/returntoravnica/PackRat.java | 33 +++-- .../sets/riseoftheeldrazi/SplinterTwin.java | 40 +++--- .../mage/sets/scarsofmirrodin/MimicVat.java | 32 ++--- .../sets/scarsofmirrodin/MyrPropagator.java | 55 +------- .../mage/sets/shadowmoor/RhysTheRedeemed.java | 14 +- .../sets/shardsofalara/MinionReflector.java | 47 +++---- .../src/mage/sets/thedark/DanceOfMany.java | 56 ++++---- .../src/mage/sets/worldwake/NemesisTrap.java | 39 +++-- .../mage/sets/zendikar/RiteOfReplication.java | 48 +------ .../cards/copy/KikiJikiMirrorBreakerTest.java | 55 ++++++++ .../abilities/effects/CopyCardEffect.java | 51 ------- ...tTokenOntoBattlefieldCopySourceEffect.java | 47 +++++++ .../PutTokenOntoBattlefieldCopySource.java | 94 ------------- ...tTokenOntoBattlefieldCopyTargetEffect.java | 77 +++++++--- Mage/src/mage/game/permanent/Permanent.java | 1 + .../util/functions/CopyTokenFunction.java | 1 + 36 files changed, 477 insertions(+), 971 deletions(-) delete mode 100644 Mage/src/mage/abilities/effects/CopyCardEffect.java create mode 100644 Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java delete mode 100644 Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java index 2cf5cc1ea2b..8eef2b60f3a 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KikiJikiMirrorBreaker.java @@ -35,6 +35,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -47,10 +48,8 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SupertypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -111,25 +110,20 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); + sacrificeEffect.setTargetPointer(new FixedTarget(addedToken.getId())); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } diff --git a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java index e975b2db45b..f1d4582bf77 100644 --- a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java +++ b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java @@ -30,11 +30,13 @@ package mage.sets.commander; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -48,11 +50,8 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.stack.Spell; import mage.players.Player; -import mage.util.CardUtil; /** * @@ -62,6 +61,7 @@ public class RikuOfTwoReflections extends CardImpl { private static final FilterSpell filter = new FilterSpell("an instant or sorcery spell"); private static final FilterControlledCreaturePermanent filterPermanent = new FilterControlledCreaturePermanent("another nontoken creature"); + static { filter.add(Predicates.or( new CardTypePredicate(CardType.INSTANT), @@ -85,12 +85,10 @@ public class RikuOfTwoReflections extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new RikuOfTwoReflectionsCopyEffect(), new ManaCostsImpl("{U}{R}")), filter, false, true)); // Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield. - Ability ability = new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, new RikuOfTwoReflectionsCopyTokenEffect(),filterPermanent, false, SetTargetPointer.PERMANENT, - "Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield.", - true); - ability.addCost(new ManaCostsImpl("{G}{U}")); - this.addAbility(ability); + Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), + new ManaCostsImpl("{G}{U}"), "Put a token that's a copy of that creature onto the battlefield?"); + effect.setText("you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield"); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filterPermanent, false, SetTargetPointer.PERMANENT, null)); } public RikuOfTwoReflections(final RikuOfTwoReflections card) { @@ -139,32 +137,3 @@ class RikuOfTwoReflectionsCopyEffect extends OneShotEffect { return new RikuOfTwoReflectionsCopyEffect(this); } } - -class RikuOfTwoReflectionsCopyTokenEffect extends OneShotEffect { - - public RikuOfTwoReflectionsCopyTokenEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of that creature onto the battlefield"; - } - - public RikuOfTwoReflectionsCopyTokenEffect(final RikuOfTwoReflectionsCopyTokenEffect effect) { - super(effect); - } - - @Override - public RikuOfTwoReflectionsCopyTokenEffect copy() { - return new RikuOfTwoReflectionsCopyTokenEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java b/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java index fed6939173d..9fc589a8482 100644 --- a/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java +++ b/Mage.Sets/src/mage/sets/commander/Spawnwrithe.java @@ -30,7 +30,7 @@ package mage.sets.commander; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.common.PutTokenOntoBattlefieldCopySource; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -53,7 +53,7 @@ public class Spawnwrithe extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Whenever Spawnwrithe deals combat damage to a player, put a token that's a copy of Spawnwrithe onto the battlefield. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySource(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySourceEffect(), false)); } diff --git a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java index a3c53ae6d78..77840101add 100644 --- a/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java +++ b/Mage.Sets/src/mage/sets/commander2014/FeldonOfTheThirdPath.java @@ -36,8 +36,8 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbil import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.abilities.keyword.HasteAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -47,10 +47,8 @@ import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -105,19 +103,12 @@ class FeldonOfTheThirdPathEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { - EmptyToken token = new EmptyToken(); - // TODO: This fails if a card will be copied, that uses adjustTargets() method. - CardUtil.copyTo(token).from(card); - - if (!token.getCardType().contains(CardType.ARTIFACT)) { - token.getCardType().add(CardType.ARTIFACT); - } - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent permanent = game.getPermanent(token.getLastAddedToken()); - if (permanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), CardType.ARTIFACT, true); + effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(new FixedTarget(permanent, game)); + sacrificeEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java b/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java index 7361afc4052..76dca6a09b9 100644 --- a/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java +++ b/Mage.Sets/src/mage/sets/conflux/MirrorSigilSergeant.java @@ -29,28 +29,21 @@ package mage.sets.conflux; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; /** * @@ -63,7 +56,7 @@ public class MirrorSigilSergeant extends CardImpl { static { filter.add(new ColorPredicate(ObjectColor.BLUE)); } - + private static final String rule = "At the beginning of your upkeep, if you control a blue permanent, you may put a token that's a copy of Mirror-Sigil Sergeant onto the battlefield."; public MirrorSigilSergeant(UUID ownerId) { @@ -79,7 +72,9 @@ public class MirrorSigilSergeant extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, if you control a blue permanent, you may put a token that's a copy of Mirror-Sigil Sergeant onto the battlefield. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new MirrorSigilSergeantEffect(), TargetController.YOU, true); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("you may put a token that's a copy of {this} onto the battlefield"); + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, true); this.addAbility(new ConditionalTriggeredAbility(ability, new PermanentsOnTheBattlefieldCondition(filter), rule)); } @@ -93,41 +88,3 @@ public class MirrorSigilSergeant extends CardImpl { return new MirrorSigilSergeant(this); } } - -class MirrorSigilSergeantEffect extends OneShotEffect { - - public MirrorSigilSergeantEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of {this} onto the battlefield"; - } - - public MirrorSigilSergeantEffect(final MirrorSigilSergeantEffect effect) { - super(effect); - } - - @Override - public MirrorSigilSergeantEffect copy() { - return new MirrorSigilSergeantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - UUID targetId = source.getSourceId(); - if (targetId != null && player != null) { - MageObject target = game.getPermanent(targetId); - if (target == null) { - target = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (target != null) { - if (target instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent) target); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/darkascension/Seance.java b/Mage.Sets/src/mage/sets/darkascension/Seance.java index 61a81d26e38..5f848ebe8ab 100644 --- a/Mage.Sets/src/mage/sets/darkascension/Seance.java +++ b/Mage.Sets/src/mage/sets/darkascension/Seance.java @@ -34,6 +34,7 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -44,11 +45,9 @@ import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -97,28 +96,21 @@ class SeanceEffect extends OneShotEffect { Card card = game.getCard(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { - if (controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true)) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - - if (!token.hasSubtype("Spirit")) { - token.getSubtype().add("Spirit"); - } - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + if (controller.moveCards(card, null, Zone.EXILED, source, game)) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setAdditionalSubType("Spirit"); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); } } - return true; } diff --git a/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java b/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java index 1f941488cf9..29125cb5e58 100644 --- a/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java +++ b/Mage.Sets/src/mage/sets/dissension/SproutingPhytohydra.java @@ -30,7 +30,8 @@ package mage.sets.dissension; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.abilities.effects.common.PutTokenOntoBattlefieldCopySource; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -54,7 +55,9 @@ public class SproutingPhytohydra extends CardImpl { // Defender this.addAbility(DefenderAbility.getInstance()); // Whenever Sprouting Phytohydra is dealt damage, you may put a token that's a copy of Sprouting Phytohydra onto the battlefield. - this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySource(), false)); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("you may put a token that's a copy of {this} onto the battlefield"); + this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, effect, true)); } public SproutingPhytohydra(final SproutingPhytohydra card) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java index 34aacc41a2a..0553d75993b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProgenitorMimic.java @@ -29,30 +29,22 @@ package mage.sets.dragonsmaze; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceMatchesFilterCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.EntersBattlefieldEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CopyEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.CopyPermanentEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; import mage.util.functions.AbilityApplier; /** @@ -78,9 +70,11 @@ public class ProgenitorMimic extends CardImpl { // You may have Progenitor Mimic enter the battlefield as a copy of any creature on the battlefield // except it gains "At the beginning of your upkeep, if this creature isn't a token, // put a token onto the battlefield that's a copy of this creature." + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(); + effect.setText("put a token onto the battlefield that's a copy of this creature"); AbilityApplier applier = new AbilityApplier( new ConditionalTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new ProgenitorMimicCopyEffect(), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false), new SourceMatchesFilterCondition(filter), "At the beginning of your upkeep, if this creature isn't a token, put a token onto the battlefield that's a copy of this creature.") ); @@ -100,55 +94,3 @@ public class ProgenitorMimic extends CardImpl { return new ProgenitorMimic(this); } } - -class ProgenitorMimicCopyEffect extends OneShotEffect { - - public ProgenitorMimicCopyEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token onto the battlefield that's a copy of this creature"; - } - - public ProgenitorMimicCopyEffect(final ProgenitorMimicCopyEffect effect) { - super(effect); - } - - @Override - public ProgenitorMimicCopyEffect copy() { - return new ProgenitorMimicCopyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent copyFromPermanent = null; - // handle copies of copies - for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { - if (effect instanceof CopyEffect) { - CopyEffect copyEffect = (CopyEffect) effect; - // there is another copy effect that our targetPermanent copies stats from - if (copyEffect.getSourceId().equals(source.getSourceId())) { - MageObject oldBluePrint = ((CopyEffect) effect).getTarget(); - if (oldBluePrint instanceof Permanent) { - // copy it and apply the applier if any - copyFromPermanent = (Permanent) oldBluePrint; - } - } - } - } - if (copyFromPermanent == null) { - // if it was no copy of copy take the target itself - copyFromPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - - if (copyFromPermanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFromPermanent); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent newPermanentToken = game.getPermanent(token.getLastAddedToken()); - if (newPermanentToken != null) { - game.copyPermanent(copyFromPermanent, newPermanentToken, source, null); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java index 6225c113df0..bda88f79158 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/CloneLegion.java @@ -30,6 +30,7 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -37,10 +38,9 @@ import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetPlayer; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -91,9 +91,9 @@ class CloneLegionEffect extends OneShotEffect { if (controller != null && targetPlayer != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), targetPlayer.getId(), game)) { if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java index 5eb71f49bdf..f43c40388d0 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/MirrorMockery.java @@ -35,6 +35,7 @@ import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.AttachmentType; @@ -43,11 +44,9 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -105,16 +104,13 @@ class MirrorMockeryEffect extends OneShotEffect { } Permanent enchanted = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); if (enchanted != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(enchanted); - - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(enchanted, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + if (addedToken != null) { ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/eventide/SpittingImage.java b/Mage.Sets/src/mage/sets/eventide/SpittingImage.java index a9eda3c7deb..08a233a9427 100644 --- a/Mage.Sets/src/mage/sets/eventide/SpittingImage.java +++ b/Mage.Sets/src/mage/sets/eventide/SpittingImage.java @@ -30,6 +30,7 @@ package mage.sets.eventide; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.RetraceAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -45,7 +46,7 @@ import mage.util.CardUtil; /** * * @author jeffwadsworth - + * */ public class SpittingImage extends CardImpl { @@ -54,12 +55,12 @@ public class SpittingImage extends CardImpl { this.expansionSetCode = "EVE"; // Put a token that's a copy of target creature onto the battlefield. - this.getSpellAbility().addEffect(new SpittingImageEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - + // Retrace (You may cast this card from your graveyard by discarding a land card in addition to paying its other costs.) this.addAbility(new RetraceAbility(this)); - + } public SpittingImage(final SpittingImage card) { diff --git a/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java b/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java index 721d26b3dd4..888ce088d0b 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java +++ b/Mage.Sets/src/mage/sets/fatereforged/FlamerushRider.java @@ -36,6 +36,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.DashAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -46,11 +47,9 @@ import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -113,16 +112,13 @@ class FlamerushRiderEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); if (controller != null && permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), true, true); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(effect), false).apply(game, source); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java b/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java index 13914d02aac..d71e5462ee2 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java +++ b/Mage.Sets/src/mage/sets/fatereforged/SupplantForm.java @@ -28,18 +28,13 @@ package mage.sets.fatereforged; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; /** * @@ -54,7 +49,9 @@ public class SupplantForm extends CardImpl { // Return target creature to its owner's hand. You put a token onto the battlefield that's a copy of that creature. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new SupplantFormEffect()); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setText("You put a token onto the battlefield that's a copy of that creature"); + this.getSpellAbility().addEffect(effect); } public SupplantForm(final SupplantForm card) { @@ -66,32 +63,3 @@ public class SupplantForm extends CardImpl { return new SupplantForm(this); } } - -class SupplantFormEffect extends OneShotEffect { - - public SupplantFormEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "You put a token onto the battlefield that's a copy of that creature"; - } - - public SupplantFormEffect(final SupplantFormEffect effect) { - super(effect); - } - - @Override - public SupplantFormEffect copy() { - return new SupplantFormEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetPermanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (targetPermanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(targetPermanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java b/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java index 67b1fd6ab7c..c0d4b180daf 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java +++ b/Mage.Sets/src/mage/sets/gatecrash/GiantAdephage.java @@ -28,22 +28,13 @@ package mage.sets.gatecrash; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; +import mage.constants.CardType; +import mage.constants.Rarity; /** * @@ -63,7 +54,7 @@ public class GiantAdephage extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Giant Adephage deals combat damage to a player, put a token onto the battlefield that is a copy of Giant Adephage. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GiantAdephageCopyEffect(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTokenOntoBattlefieldCopySourceEffect(), false)); } @@ -76,40 +67,3 @@ public class GiantAdephage extends CardImpl { return new GiantAdephage(this); } } - -class GiantAdephageCopyEffect extends OneShotEffect { - - public GiantAdephageCopyEffect() { - super(Outcome.Copy); - this.staticText = "put a token onto the battlefield that is a copy of Giant Adephage"; - } - - public GiantAdephageCopyEffect(final GiantAdephageCopyEffect effect) { - super(effect); - } - - @Override - public GiantAdephageCopyEffect copy() { - return new GiantAdephageCopyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } else { // maybe it's token - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java b/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java index af874fafb0d..6ea81331c7e 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java +++ b/Mage.Sets/src/mage/sets/gatecrash/StolenIdentity.java @@ -28,23 +28,15 @@ package mage.sets.gatecrash; import java.util.UUID; - +import mage.abilities.effects.common.CipherEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CipherEffect; -import mage.cards.CardImpl; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; -import mage.util.CardUtil; /** * @@ -53,17 +45,17 @@ import mage.util.CardUtil; public class StolenIdentity extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("artifact or creature"); + static { filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE))); } - + public StolenIdentity(UUID ownerId) { super(ownerId, 53, "Stolen Identity", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); this.expansionSetCode = "GTC"; - // Put a token onto the battlefield that's a copy of target artifact or creature. - this.getSpellAbility().addEffect(new StolenIdentityEffect()); + this.getSpellAbility().addEffect(new PutTokenOntoBattlefieldCopyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); // Cipher this.getSpellAbility().addEffect(new CipherEffect()); @@ -78,42 +70,3 @@ public class StolenIdentity extends CardImpl { return new StolenIdentity(this); } } - - -class StolenIdentityEffect extends OneShotEffect { - - - - public StolenIdentityEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Put a token onto the battlefield that's a copy of target artifact or creature"; - } - - public StolenIdentityEffect(final StolenIdentityEffect effect) { - super(effect); - } - - @Override - public StolenIdentityEffect copy() { - return new StolenIdentityEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - return true; - } - - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java b/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java index d088e33c83c..4565580087f 100644 --- a/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java +++ b/Mage.Sets/src/mage/sets/innistrad/BackFromTheBrink.java @@ -28,24 +28,23 @@ package mage.sets.innistrad; import java.util.UUID; - +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.CostImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.common.ActivateAsSorceryActivatedAbility; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.players.Player; -import mage.game.permanent.token.EmptyToken; +import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -57,9 +56,10 @@ public class BackFromTheBrink extends CardImpl { super(ownerId, 44, "Back from the Brink", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{U}"); this.expansionSetCode = "ISD"; - // Exile a creature card from your graveyard and pay its mana cost: Put a token onto the battlefield that's a copy of that card. Activate this ability only any time you could cast a sorcery. - this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new BackFromTheBrinkEffect(), new BackFromTheBrinkCost())); + Effect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setText("Put a token onto the battlefield that's a copy of that card"); + this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new BackFromTheBrinkCost())); } @@ -73,40 +73,12 @@ public class BackFromTheBrink extends CardImpl { } } -class BackFromTheBrinkEffect extends OneShotEffect { - - public BackFromTheBrinkEffect () { - super(Outcome.PutCreatureInPlay); - staticText = "Put a token onto the battlefield that's a copy of that card. Activate this ability only any time you could cast a sorcery"; - } - - public BackFromTheBrinkEffect(final BackFromTheBrinkEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(this.targetPointer.getFirst(game, source)); - if (card != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } - - @Override - public BackFromTheBrinkEffect copy() { - return new BackFromTheBrinkEffect(this); - } - -} - class BackFromTheBrinkCost extends CostImpl { public BackFromTheBrinkCost() { - this.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); + Target target = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard")); + target.setNotTarget(true); + this.addTarget(target); this.text = "Exile a creature card from your graveyard and pay its mana cost"; } @@ -127,10 +99,10 @@ class BackFromTheBrinkCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { - Player player = game.getPlayer(controllerId); - if (player != null) { - Card card = player.getGraveyard().get(targets.getFirstTarget(), game); - if (card != null && card.moveToZone(Zone.EXILED, sourceId, game, false)) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + Card card = controller.getGraveyard().get(targets.getFirstTarget(), game); + if (card != null && controller.moveCards(card, null, Zone.EXILED, ability, game)) { ability.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId())); paid = card.getManaCost().pay(ability, game, sourceId, controllerId, noMana); } @@ -139,4 +111,4 @@ class BackFromTheBrinkCost extends CostImpl { return paid; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java index ae132b9f1a8..da065db84d5 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/Twinflame.java @@ -34,7 +34,7 @@ import mage.abilities.abilityword.StriveAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -42,11 +42,9 @@ import mage.constants.Rarity; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -58,13 +56,12 @@ public class Twinflame extends CardImpl { super(ownerId, 115, "Twinflame", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{R}"); this.expansionSetCode = "JOU"; - // Strive - Twinflame costs 2R more to cast for each target beyond the first. this.addAbility(new StriveAbility("{2}{R}")); // Choose any number of target creatures you control. For each of them, put a token that's a copy of that creature onto the battlefield. Those tokens have haste. Exile them at the beginning of the next end step. this.getSpellAbility().addEffect(new TwinflameCopyEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, new FilterControlledCreaturePermanent(), false)); - + } public Twinflame(final Twinflame card) { @@ -78,44 +75,43 @@ public class Twinflame extends CardImpl { } class TwinflameCopyEffect extends OneShotEffect { - + public TwinflameCopyEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Choose any number of target creatures you control. For each of them, put a token that's a copy of that creature onto the battlefield. Those tokens have haste. Exile them at the beginning of the next end step"; } - + public TwinflameCopyEffect(final TwinflameCopyEffect effect) { super(effect); } - + @Override public TwinflameCopyEffect copy() { return new TwinflameCopyEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for(UUID creatureId: this.getTargetPointer().getTargets(game, source)) { + for (UUID creatureId : this.getTargetPointer().getTargets(game, source)) { Permanent creature = game.getPermanentOrLKIBattlefield(creatureId); if (creature != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(creature); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(creature, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } + } } - return true; + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java index 0ae3d362a29..801eaf76dca 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java +++ b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java @@ -37,6 +37,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -47,9 +48,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.TargetCard; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -175,9 +176,9 @@ class SoulFoundryEffect extends OneShotEffect { Card imprinted = game.getCard(soulFoundry.getImprinted().get(0)); if (imprinted != null && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(imprinted); - return token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(imprinted.getId(), imprinted.getZoneChangeCounter(game))); + return effect.apply(game, source); } } } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java index 6bc55444c9a..93d3b051d17 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/Mirrorworks.java @@ -1,16 +1,16 @@ /* * Copyright 2011 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,7 +20,7 @@ * 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. @@ -28,26 +28,20 @@ package mage.sets.mirrodinbesieged; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; -import mage.game.permanent.token.EmptyToken; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; /** * @@ -55,13 +49,23 @@ import mage.util.CardUtil; */ public class Mirrorworks extends CardImpl { + private final static FilterArtifactPermanent filter = new FilterArtifactPermanent("another nontoken artifact"); + + static { + filter.add(new AnotherPredicate()); + filter.add(Predicates.not(new TokenPredicate())); + } + public Mirrorworks(UUID ownerId) { super(ownerId, 114, "Mirrorworks", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); this.expansionSetCode = "MBS"; // Whenever another nontoken artifact enters the battlefield under your control, you may pay {2}. // If you do, put a token that's a copy of that artifact onto the battlefield. - this.addAbility(new MirrorworksAbility()); + Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), + new ManaCostsImpl("{2}"), "Put a token that's a copy of that artifact onto the battlefield?"); + effect.setText("you may pay {2}. If you do, put a token that's a copy of that artifact onto the battlefield"); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filter, false, SetTargetPointer.PERMANENT, null)); } public Mirrorworks(final Mirrorworks card) { @@ -74,90 +78,3 @@ public class Mirrorworks extends CardImpl { } } - -class MirrorworksAbility extends TriggeredAbilityImpl { - - public MirrorworksAbility() { - super(Zone.BATTLEFIELD, new MirrorworksEffect()); - } - - public MirrorworksAbility(final MirrorworksAbility ability) { - super(ability); - } - - @Override - public MirrorworksAbility copy() { - return new MirrorworksAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.getControllerId().equals(getControllerId()) - && !(permanent instanceof PermanentToken) - && permanent.getCardType().contains(CardType.ARTIFACT)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever another nontoken artifact enters the battlefield under your control, you may pay {2}. If you do, put a token that's a copy of that artifact onto the battlefield"; - } - -} - -class MirrorworksEffect extends OneShotEffect { - - public MirrorworksEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of that artifact onto the battlefield"; - } - - public MirrorworksEffect(final MirrorworksEffect effect) { - super(effect); - } - - @Override - public MirrorworksEffect copy() { - return new MirrorworksEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - UUID targetId = targetPointer.getFirst(game, source); - if (targetId != null && player != null) { - MageObject target = game.getPermanent(targetId); - if (target == null) { - target = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (target != null) { - Cost cost = new ManaCostsImpl("{2}"); - if (player.chooseUse(outcome, new StringBuilder("Pay ").append(cost.getText()).append(" and put a token copy of ").append(target.getName()).append(" onto the battlefield").toString(), source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - if (target instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)target); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - } - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java b/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java index c3e250f828c..660b1a980ae 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java +++ b/Mage.Sets/src/mage/sets/planarchaos/Chronozoa.java @@ -34,7 +34,8 @@ import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.CopyCardEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VanishingSacrificeAbility; @@ -47,14 +48,11 @@ import mage.counters.CounterType; /** * * @author Gal Lerman - + * */ public class Chronozoa extends CardImpl { - private static final int timeCounters = 3; - private static final int numCopies = 2; - - public Chronozoa(UUID ownerId) { + public Chronozoa(UUID ownerId) { super(ownerId, 37, "Chronozoa", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.expansionSetCode = "PLC"; this.subtype.add("Illusion"); @@ -64,15 +62,17 @@ public class Chronozoa extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Vanishing 3 - Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIME.createInstance(timeCounters))); + Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIME.createInstance(3))); ability.setRuleVisible(false); this.addAbility(ability); - this.addAbility(new VanishingUpkeepAbility(timeCounters)); + this.addAbility(new VanishingUpkeepAbility(3)); this.addAbility(new VanishingSacrificeAbility()); // When Chronozoa is put into a graveyard from play, if it had no time counters on it, put two tokens into play that are copies of it. - this.addAbility(new ConditionalTriggeredAbility(new DiesCreatureTriggeredAbility(new CopyCardEffect(this, numCopies), false), - new LastTimeCounterRemovedCondition(), - "When {this} dies, if it had no time counters on it, put two tokens that are copies of it onto the battlefield.")); + Effect effect = new PutTokenOntoBattlefieldCopySourceEffect(2); + effect.setText("put two tokens into play that are copies of it"); + this.addAbility(new ConditionalTriggeredAbility(new DiesCreatureTriggeredAbility(effect, false), + new LastTimeCounterRemovedCondition(), + "When {this} dies, if it had no time counters on it, put two tokens that are copies of it onto the battlefield.")); } public Chronozoa(final Chronozoa card) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java b/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java index 266521195b0..2d32fcf5aaf 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/PackRat.java @@ -28,11 +28,6 @@ package mage.sets.returntoravnica; import java.util.UUID; - -import mage.abilities.effects.CopyCardEffect; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -40,32 +35,37 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; /** * * The token will copy Pack Rat's two abilities. Its power and toughness will be - * equal to the number of Rats you control (not the number of Rats you controlled - * when the token entered the battlefield). It will also be able to create copies - * of itself. + * equal to the number of Rats you control (not the number of Rats you + * controlled when the token entered the battlefield). It will also be able to + * create copies of itself. * - * The token won't copy counters on Pack Rat, nor will it copy other effects that - * have changed Pack Rat's power, toughness, types, color, or so on. Normally, - * this means the token will simply be a Pack Rat. But if any copy effects have - * affected that Pack Rat, they're taken into account. + * The token won't copy counters on Pack Rat, nor will it copy other effects + * that have changed Pack Rat's power, toughness, types, color, or so on. + * Normally, this means the token will simply be a Pack Rat. But if any copy + * effects have affected that Pack Rat, they're taken into account. * - * If Pack Rat leaves the battlefield before its activated ability resolves, - * the token will still enter the battlefield as a copy of Pack Rat, using - * Pack Rat's copiable values from when it was last on the battlefield. + * If Pack Rat leaves the battlefield before its activated ability resolves, the + * token will still enter the battlefield as a copy of Pack Rat, using Pack + * Rat's copiable values from when it was last on the battlefield. * * * @author LevelX2 */ public class PackRat extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Rats you control"); static { @@ -83,7 +83,7 @@ public class PackRat extends CardImpl { // Pack Rat's power and toughness are each equal to the number of Rats you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); // {2}{B}, Discard a card: Put a token onto the battlefield that's a copy of Pack Rat. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyCardEffect(this, 1), new ManaCostsImpl("{2}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySourceEffect(), new ManaCostsImpl("{2}{B}")); ability.addCost(new DiscardCardCost()); this.addAbility(ability); } @@ -97,4 +97,3 @@ public class PackRat extends CardImpl { return new PackRat(this); } } - diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java index f3025d07eb1..ee1aa00718b 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SplinterTwin.java @@ -28,11 +28,6 @@ package mage.sets.riseoftheeldrazi; import java.util.UUID; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -42,17 +37,20 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; -import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; +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; -import mage.game.permanent.token.EmptyToken; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -65,7 +63,6 @@ public class SplinterTwin extends CardImpl { this.expansionSetCode = "ROE"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -107,19 +104,18 @@ class SplinterTwinEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java index 33664325db6..324ecd84756 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MimicVat.java @@ -37,7 +37,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -50,10 +50,8 @@ import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @author nantuko @@ -204,23 +202,17 @@ class MimicVatCreateTokenEffect extends OneShotEffect { if (permanent.getImprinted().size() > 0) { Card card = game.getCard(permanent.getImprinted().get(0)); if (card != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(card); - - token.addAbility(HasteAbility.getInstance()); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); } return true; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java index 83037aeec91..ed639b1d68f 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrPropagator.java @@ -27,24 +27,17 @@ */ package mage.sets.scarsofmirrodin; -import mage.constants.CardType; -import mage.constants.Rarity; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PutTokenOntoBattlefieldCopySourceEffect; import mage.cards.CardImpl; -import mage.constants.Outcome; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; - -import java.util.UUID; -import mage.MageObject; /** * @@ -61,7 +54,7 @@ public class MyrPropagator extends CardImpl { this.toughness = new MageInt(1); // {3}, {tap}: Put a token that's a copy of Myr Propagator onto the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MyrPropagatorCreateTokenEffect(), new GenericManaCost(3)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTokenOntoBattlefieldCopySourceEffect(), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -75,41 +68,3 @@ public class MyrPropagator extends CardImpl { return new MyrPropagator(this); } } - -class MyrPropagatorCreateTokenEffect extends OneShotEffect { - - public MyrPropagatorCreateTokenEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Put a token that's a copy of {this} onto the battlefield"; - } - - public MyrPropagatorCreateTokenEffect(final MyrPropagatorCreateTokenEffect effect) { - super(effect); - } - - @Override - public MyrPropagatorCreateTokenEffect copy() { - return new MyrPropagatorCreateTokenEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } else { // maybe it's token - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java b/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java index c66ba3594b3..14d5e8ab7c1 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/RhysTheRedeemed.java @@ -35,6 +35,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -45,10 +46,9 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.game.permanent.token.Token; import mage.players.Player; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -113,13 +113,13 @@ class RhysTheRedeemedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { if (permanent.getControllerId().equals(source.getControllerId())) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java b/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java index d7980605090..47f5c126b5d 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/MinionReflector.java @@ -28,17 +28,22 @@ package mage.sets.shardsofalara; import java.util.UUID; - -import mage.constants.*; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; 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.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -46,9 +51,7 @@ import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -57,10 +60,11 @@ import mage.util.CardUtil; public class MinionReflector extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature"); - static{ + + static { filter.add(Predicates.not(new TokenPredicate())); } - + public MinionReflector(UUID ownerId) { super(ownerId, 211, "Minion Reflector", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); this.expansionSetCode = "ALA"; @@ -83,7 +87,6 @@ public class MinionReflector extends CardImpl { class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { - public MinionReflectorTriggeredAbility() { super(new MinionReflectorEffect(), new FilterControlledCreaturePermanent(), "Whenever a nontoken creature enters the battlefield under your control, you may pay {2}. If you do, put a token that's a copy of that creature onto the battlefield. That token has haste and \"At the beginning of the end step, sacrifice this permanent"); filter.add(Predicates.not(new TokenPredicate())); @@ -99,7 +102,7 @@ class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbili UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); if (filter.match(permanent, getSourceId(), getControllerId(), game)) { - for(Effect effect : this.getEffects()){ + for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(targetId)); } return true; @@ -108,16 +111,12 @@ class MinionReflectorTriggeredAbility extends EntersBattlefieldAllTriggeredAbili return false; } - - @Override public MinionReflectorTriggeredAbility copy() { return new MinionReflectorTriggeredAbility(this); } - - -} +} class MinionReflectorEffect extends OneShotEffect { @@ -137,18 +136,16 @@ class MinionReflectorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - + Permanent permanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - - token.addAbility(HasteAbility.getInstance()); - token.addAbility(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, true)); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, true), Duration.Custom); + continuousEffect.setTargetPointer(new FixedTarget(addedToken.getId())); + game.addEffect(continuousEffect, source); + } return true; } diff --git a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java index 758f9baf86d..de05affa1de 100644 --- a/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java +++ b/Mage.Sets/src/mage/sets/thedark/DanceOfMany.java @@ -27,6 +27,7 @@ */ package mage.sets.thedark; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -36,12 +37,14 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -53,10 +56,9 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @@ -113,22 +115,22 @@ class DanceOfManyCreateTokenCopyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - Permanent permanentToken = game.getPermanent(token.getLastAddedToken()); - game.getState().setValue(source.getSourceId() + "_token", permanentToken); - Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many"); - sacrificeEffect.setTargetPointer(new FixedTarget(game.getPermanent(source.getSourceId()).getId())); - LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); - ContinuousEffect effect = new GainAbilityTargetEffect(triggerAbility, Duration.WhileOnBattlefield); - effect.setTargetPointer(new FixedTarget(token.getLastAddedToken())); - game.addEffect(effect, source); + Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (permanent != null && sourceObject != null) { + + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanent()); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many"); + sacrificeEffect.setTargetPointer(new FixedTarget(sourceObject, game)); + LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(triggerAbility, Duration.WhileOnBattlefield); + continuousEffect.setTargetPointer(new FixedTarget(addedToken, game)); + game.addEffect(continuousEffect, source); + } return true; } return false; @@ -153,11 +155,17 @@ class DanceOfManyExileTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent tokenPermanent = (Permanent) game.getState().getValue(source.getSourceId() + "_token"); - if (tokenPermanent != null) { - Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(tokenPermanent.getId())); - return effect.apply(game, source); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List tokenPermanents = (List) game.getState().getValue(source.getSourceId() + "_token"); + if (tokenPermanents != null) { + Cards cards = new CardsImpl(); + for (Permanent permanent : tokenPermanents) { + cards.add(permanent); + } + controller.moveCards(cards, null, Zone.EXILED, source, game); + return true; + } } return false; } diff --git a/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java b/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java index 5665494edbe..95b7dead30f 100644 --- a/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/NemesisTrap.java @@ -29,28 +29,29 @@ package mage.sets.worldwake; import java.util.UUID; import mage.ObjectColor; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; import mage.players.Player; import mage.target.common.TargetAttackingCreature; -import mage.util.CardUtil; +import mage.target.targetpointer.FixedTarget; /** * @@ -59,19 +60,17 @@ import mage.util.CardUtil; public class NemesisTrap extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("If a white creature is attacking"); - + static { filter.add(new ColorPredicate(ObjectColor.WHITE)); filter.add(new AttackingPredicate()); } - - + public NemesisTrap(UUID ownerId) { super(ownerId, 61, "Nemesis Trap", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{4}{B}{B}"); this.expansionSetCode = "WWK"; this.subtype.add("Trap"); - // If a white creature is attacking, you may pay {B}{B} rather than pay Nemesis Trap's mana cost. this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{B}{B}"), new PermanentsOnTheBattlefieldCondition(filter, PermanentsOnTheBattlefieldCondition.CountType.MORE_THAN, 0, false))); @@ -111,11 +110,21 @@ class NemesisTrapEffect extends OneShotEffect { Permanent targetedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && targetedCreature != null) { - controller.moveCardToExileWithInfo(targetedCreature, null, null, source.getSourceId(), game, Zone.BATTLEFIELD, true); - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(targetedCreature); - token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - token.addAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ExileSourceEffect())); + // exile target + controller.moveCards(targetedCreature, null, Zone.EXILED, source, game); + // create token + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(); + effect.setTargetPointer(new FixedTarget(targetedCreature, game)); + effect.apply(game, source); + for (Permanent addedToken : effect.getAddedPermanent()) { + Effect exileEffect = new ExileTargetEffect("Exile " + addedToken.getName() + " at the beginning of the next end step"); + exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java b/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java index fd51ca2ee7a..85152ecc7c4 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java +++ b/Mage.Sets/src/mage/sets/zendikar/RiteOfReplication.java @@ -28,21 +28,14 @@ package mage.sets.zendikar; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.abilities.keyword.KickerAbility; 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; -import mage.game.permanent.token.EmptyToken; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; /** * @@ -59,8 +52,8 @@ public class RiteOfReplication extends CardImpl { // Put a token that's a copy of target creature onto the battlefield. If Rite of Replication was kicked, put five of those tokens onto the battlefield instead. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new RiteOfReplicationEffect(5), - new RiteOfReplicationEffect(1), KickedCondition.getInstance(), + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new PutTokenOntoBattlefieldCopyTargetEffect(null, null, false, 5), + new PutTokenOntoBattlefieldCopyTargetEffect(), KickedCondition.getInstance(), "Put a token that's a copy of target creature onto the battlefield. If {this} was kicked, put five of those tokens onto the battlefield instead")); } @@ -73,38 +66,3 @@ public class RiteOfReplication extends CardImpl { return new RiteOfReplication(this); } } - -class RiteOfReplicationEffect extends OneShotEffect { - - private final int amount; - - public RiteOfReplicationEffect(int amount) { - super(Outcome.PutCreatureInPlay); - this.amount = amount; - } - - public RiteOfReplicationEffect(final RiteOfReplicationEffect effect) { - super(effect); - this.amount = effect.amount; - } - - @Override - public RiteOfReplicationEffect copy() { - return new RiteOfReplicationEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - } - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - token.putOntoBattlefield(amount, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java index d252e8a6a79..ff78a4dcf31 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/KikiJikiMirrorBreakerTest.java @@ -27,8 +27,13 @@ */ package org.mage.test.cards.copy; +import mage.abilities.keyword.HasteAbility; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -134,4 +139,54 @@ public class KikiJikiMirrorBreakerTest extends CardTestPlayerBase { } + /** + * + * Kiki-Jiki, Mirror Breaker tokens are not entering with haste... + * + * I just tried to reproduce this but I was not able-- + * + * In the game in question I used a card (Body Double) to target a card in a + * graveyard (don't remember the name) and copy it + * + * I then used Kiki to copy Body Double that copied a graveyard card of my + * opponent---that copy did not have haste... + */ + @Test + public void testCopyBodyDouble() { + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + addCard(Zone.BATTLEFIELD, playerB, "Kiki-Jiki, Mirror Breaker", 1); + // {T}: Draw two cards. Target opponent gains control of Humble Defector. Activate this ability only during your turn. + addCard(Zone.HAND, playerB, "Body Double", 1); // {4}{U} + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Body Double"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Put a token that's a copy of target nonlegendary creature you control onto the battlefield. That token has haste. Sacrifice it at the beginning of the next end step."); + + attack(2, playerB, "Silvercoat Lion"); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 2); // one from Body Double and one from Kiki + + Permanent kikiCopy = null; + for (Permanent permanent : currentGame.getState().getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), currentGame)) { + if (permanent.getName().equals("Silvercoat Lion") && (permanent instanceof PermanentToken)) { + kikiCopy = permanent; + break; + } + } + if (kikiCopy != null) { + Assert.assertEquals("Has to have haste", kikiCopy.getAbilities(currentGame).containsClass(HasteAbility.class), true); + } else { + Assert.assertEquals("Silvercoat Lion copied by Kiki is missing", kikiCopy != null, true); + } + + assertLife(playerA, 18); + assertLife(playerB, 20); + + } } diff --git a/Mage/src/mage/abilities/effects/CopyCardEffect.java b/Mage/src/mage/abilities/effects/CopyCardEffect.java deleted file mode 100644 index 3435e707673..00000000000 --- a/Mage/src/mage/abilities/effects/CopyCardEffect.java +++ /dev/null @@ -1,51 +0,0 @@ -package mage.abilities.effects; - -import mage.abilities.Ability; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.util.CardUtil; - -/** - * Created by glerman on 20/6/15. - */ -public class CopyCardEffect extends OneShotEffect { - - private final Card card; - private final int copies; - - public CopyCardEffect(Card card, int copies) { - super(Outcome.PutCreatureInPlay); - this.card = card; - this.copies = copies; - staticText = "Put a token onto the battlefield that's a copy of {this}"; - } - - public CopyCardEffect(final CopyCardEffect effect) { - super(effect); - this.card = effect.card; - this.copies = effect.copies; - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (permanent != null) { - EmptyToken newToken = new EmptyToken(); - CardUtil.copyTo(newToken).from(permanent); - return newToken.putOntoBattlefield(copies, game, source.getSourceId(), source.getControllerId()); - } - return false; - } - - @Override - public CopyCardEffect copy() { - return new CopyCardEffect(this); - } -} diff --git a/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java b/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java new file mode 100644 index 00000000000..ac09d741514 --- /dev/null +++ b/Mage/src/mage/abilities/effects/PutTokenOntoBattlefieldCopySourceEffect.java @@ -0,0 +1,47 @@ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * Created by glerman on 20/6/15. + */ +public class PutTokenOntoBattlefieldCopySourceEffect extends OneShotEffect { + + private final int number; + + public PutTokenOntoBattlefieldCopySourceEffect() { + this(1); + } + + public PutTokenOntoBattlefieldCopySourceEffect(int copies) { + super(Outcome.PutCreatureInPlay); + this.number = copies; + staticText = "put a token onto the battlefield that's a copy of {this}"; + } + + public PutTokenOntoBattlefieldCopySourceEffect(final PutTokenOntoBattlefieldCopySourceEffect effect) { + super(effect); + this.number = effect.number; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (permanent != null) { + PutTokenOntoBattlefieldCopyTargetEffect effect = new PutTokenOntoBattlefieldCopyTargetEffect(source.getControllerId(), null, false, number); + effect.setTargetPointer(new FixedTarget(source.getSourceId())); + return effect.apply(game, source); + } + return false; + } + + @Override + public PutTokenOntoBattlefieldCopySourceEffect copy() { + return new PutTokenOntoBattlefieldCopySourceEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java deleted file mode 100644 index 78ebf7f019c..00000000000 --- a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopySource.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.EmptyToken; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author LevelX2 - */ - -public class PutTokenOntoBattlefieldCopySource extends OneShotEffect { - - public PutTokenOntoBattlefieldCopySource() { - super(Outcome.PutCreatureInPlay); - this.staticText = "put a token that's a copy of {this} onto the battlefield"; - } - - public PutTokenOntoBattlefieldCopySource(final PutTokenOntoBattlefieldCopySource effect) { - super(effect); - } - - @Override - public PutTokenOntoBattlefieldCopySource copy() { - return new PutTokenOntoBattlefieldCopySource(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (thisCard != null && thisCard instanceof Permanent) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from((Permanent)thisCard); - if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(controller.getLogName()) - .append(" puts a ").append(token.getName()).append(" token ").append("onto the Battlefield").toString()); - return true; - } - } else { // maybe it's token - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (permanent != null) { - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(permanent); - if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(controller.getLogName()) - .append(" puts a ").append(token.getName()).append(" token ").append("onto the Battlefield").toString()); - return true; - } - } - } - } - return false; - } - -} \ No newline at end of file diff --git a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java index ba01a9eef9d..2cc2381a78d 100644 --- a/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutTokenOntoBattlefieldCopyTargetEffect.java @@ -36,6 +36,7 @@ import mage.abilities.Mode; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; import mage.constants.CardType; import mage.constants.Outcome; import mage.game.Game; @@ -54,32 +55,42 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { private final UUID playerId; private final CardType additionalCardType; private boolean gainsHaste; + private final int number; private List addedTokenPermanents; + private String additionalSubType; public PutTokenOntoBattlefieldCopyTargetEffect() { super(Outcome.PutCreatureInPlay); this.playerId = null; this.additionalCardType = null; this.addedTokenPermanents = new ArrayList<>(); + this.number = 1; + this.additionalSubType = null; } public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId) { this(playerId, null, false); } + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) { + this(playerId, additionalCardType, gainsHaste, 1); + } + /** * * @param playerId null the token is controlled/owned by the controller of * the source ability * @param additionalCardType the token gains tis card types in addition * @param gainsHaste the token gains haste + * @param number number of tokens to put into play */ - public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) { + public PutTokenOntoBattlefieldCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number) { super(Outcome.PutCreatureInPlay); this.playerId = playerId; this.additionalCardType = additionalCardType; this.gainsHaste = gainsHaste; this.addedTokenPermanents = new ArrayList<>(); + this.number = number; } public PutTokenOntoBattlefieldCopyTargetEffect(final PutTokenOntoBattlefieldCopyTargetEffect effect) { @@ -88,15 +99,18 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { this.additionalCardType = effect.additionalCardType; this.gainsHaste = effect.gainsHaste; this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents); + this.number = effect.number; + this.additionalSubType = effect.additionalSubType; } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + Card copyFrom; + ApplyToPermanent applier = new EmptyApplyToPermanent(); if (permanent != null) { // handle copies of copies Permanent copyFromPermanent = permanent; - ApplyToPermanent applier = new EmptyApplyToPermanent(); for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { if (effect instanceof CopyEffect) { CopyEffect copyEffect = (CopyEffect) effect; @@ -112,27 +126,37 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { } } } - - EmptyToken token = new EmptyToken(); - CardUtil.copyTo(token).from(copyFromPermanent); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) - applier.apply(game, token); - if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { - token.getCardType().add(additionalCardType); - } - if (gainsHaste) { - token.addAbility(HasteAbility.getInstance()); - } - - token.putOntoBattlefield(1, game, source.getSourceId(), playerId == null ? source.getControllerId() : playerId); - for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - addedTokenPermanents.add(tokenPermanent); - } - } - return true; + copyFrom = copyFromPermanent; + } else { + copyFrom = game.getCard(getTargetPointer().getFirst(game, source)); } - return false; + + if (permanent == null && copyFrom == null) { + return false; + } + + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(copyFrom); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer) + applier.apply(game, token); + if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) { + token.getCardType().add(additionalCardType); + } + if (gainsHaste) { + token.addAbility(HasteAbility.getInstance()); + } + if (additionalSubType != null) { + if (token.getSubtype().contains(additionalSubType)) { + token.getSubtype().add(additionalSubType); + } + } + token.putOntoBattlefield(number, game, source.getSourceId(), playerId == null ? source.getControllerId() : playerId); + for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield + Permanent tokenPermanent = game.getPermanent(tokenId); + if (tokenPermanent != null) { + addedTokenPermanents.add(tokenPermanent); + } + } + return true; } @Override @@ -142,8 +166,11 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); - sb.append("Put a token onto the battlefield that's a copy of "); + sb.append("Put a token onto the battlefield that's a copy of target "); if (mode.getTargets() != null) { sb.append(mode.getTargets().get(0).getTargetName()); } @@ -154,4 +181,8 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect { public List getAddedPermanent() { return addedTokenPermanents; } + + public void setAdditionalSubType(String additionalSubType) { + this.additionalSubType = additionalSubType; + } } diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 9a53acdc2d1..84ae5f3986f 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -347,4 +347,5 @@ public interface Permanent extends Card, Controllable { int getCreateOrder(); void setCreateOrder(int createOrder); + } diff --git a/Mage/src/mage/util/functions/CopyTokenFunction.java b/Mage/src/mage/util/functions/CopyTokenFunction.java index d7b5496320e..9344d7d0bfc 100644 --- a/Mage/src/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/mage/util/functions/CopyTokenFunction.java @@ -57,6 +57,7 @@ public class CopyTokenFunction implements Function { } // A copy contains only the attributes of the basic card or basic Token that's the base of the permanent // else gained abililies would be copied too. + MageObject sourceObj = source; if (source instanceof PermanentToken) { sourceObj = ((PermanentToken) source).getToken(); From daea667cbfacfcccfa1f959f8d7b8caf08ceb33e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 15:04:06 +0200 Subject: [PATCH 30/39] * Pyxis of Pandemonium - Fixed that the names of the face down cards were shown in the game log while moving to exile. --- Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java b/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java index ce637215919..62ce1a931bb 100644 --- a/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java +++ b/Mage.Sets/src/mage/sets/theros/PyxisOfPandemonium.java @@ -114,16 +114,14 @@ class PyxisOfPandemoniumExileEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (player != null) { if (player.getLibrary().size() > 0) { - Card card = player.getLibrary().getFromTop(game); + Card card = player.getLibrary().getFromTop(game); String exileKey = playerId.toString() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()).toString(); UUID exileId = exileIds.get(exileKey); if (exileId == null) { exileId = UUID.randomUUID(); exileIds.put(exileKey, exileId); } - player.moveCardToExileWithInfo(card, exileId, - new StringBuilder(sourceObject.getIdName() +" (").append(player.getLogName()).append(")").toString(), - source.getSourceId(), game, Zone.LIBRARY, true); + player.moveCardsToExile(card, source, game, false, exileId, sourceObject.getIdName() + " (" + player.getName() + ")"); card.setFaceDown(true, game); } } @@ -172,7 +170,7 @@ class PyxisOfPandemoniumPutOntoBattlefieldEffect extends OneShotEffect { if (exileId != null) { ExileZone exileZone = game.getState().getExile().getExileZone(exileId); if (exileZone != null) { - for(Card card: exileZone.getCards(game)) { + for (Card card : exileZone.getCards(game)) { card.setFaceDown(false, game); if (CardUtil.isPermanentCard(card)) { player.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); From 43bf34613da1a8f9967aeecbe504c7e4d016f09f Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 19:27:42 +0300 Subject: [PATCH 31/39] Combine Final Fortune's effect into AddExtraTurnControllerEffect. Implement cards: Last Chance and Warrior's Oath --- .../sets/masterseditioniv/LastChance.java | 58 +++++++++ .../src/mage/sets/portal/LastChance.java | 52 ++++++++ .../portalthreekingdoms/WarriorsOath.java | 58 +++++++++ .../sets/seventhedition/FinalFortune.java | 114 +----------------- .../src/mage/sets/starter1999/LastChance.java | 52 ++++++++ .../turn/AddExtraTurnControllerEffect.java | 69 ++++++++++- 6 files changed, 288 insertions(+), 115 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/LastChance.java create mode 100644 Mage.Sets/src/mage/sets/portal/LastChance.java create mode 100644 Mage.Sets/src/mage/sets/portalthreekingdoms/WarriorsOath.java create mode 100644 Mage.Sets/src/mage/sets/starter1999/LastChance.java diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/LastChance.java b/Mage.Sets/src/mage/sets/masterseditioniv/LastChance.java new file mode 100644 index 00000000000..1dc88330dbe --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/LastChance.java @@ -0,0 +1,58 @@ +/* + * 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.masterseditioniv; + +import java.util.UUID; +import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class LastChance extends CardImpl { + + public LastChance(UUID ownerId) { + super(ownerId, 125, "Last Chance", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{R}{R}"); + this.expansionSetCode = "ME4"; + + // Take an extra turn after this one. At the beginning of that turn's end step, you lose the game. + this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect(true)); + } + + public LastChance(final LastChance card) { + super(card); + } + + @Override + public LastChance copy() { + return new LastChance(this); + } +} diff --git a/Mage.Sets/src/mage/sets/portal/LastChance.java b/Mage.Sets/src/mage/sets/portal/LastChance.java new file mode 100644 index 00000000000..629b96cd8eb --- /dev/null +++ b/Mage.Sets/src/mage/sets/portal/LastChance.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.portal; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class LastChance extends mage.sets.masterseditioniv.LastChance { + + public LastChance(UUID ownerId) { + super(ownerId); + this.cardNumber = 141; + this.expansionSetCode = "POR"; + } + + public LastChance(final LastChance card) { + super(card); + } + + @Override + public LastChance copy() { + return new LastChance(this); + } +} diff --git a/Mage.Sets/src/mage/sets/portalthreekingdoms/WarriorsOath.java b/Mage.Sets/src/mage/sets/portalthreekingdoms/WarriorsOath.java new file mode 100644 index 00000000000..99f7170b29d --- /dev/null +++ b/Mage.Sets/src/mage/sets/portalthreekingdoms/WarriorsOath.java @@ -0,0 +1,58 @@ +/* + * 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.portalthreekingdoms; + +import java.util.UUID; +import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class WarriorsOath extends CardImpl { + + public WarriorsOath(UUID ownerId) { + super(ownerId, 124, "Warrior's Oath", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{R}{R}"); + this.expansionSetCode = "PTK"; + + // Take an extra turn after this one. At the beginning of that turn's end step, you lose the game. + this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect(true)); + } + + public WarriorsOath(final WarriorsOath card) { + super(card); + } + + @Override + public WarriorsOath copy() { + return new WarriorsOath(this); + } +} diff --git a/Mage.Sets/src/mage/sets/seventhedition/FinalFortune.java b/Mage.Sets/src/mage/sets/seventhedition/FinalFortune.java index 14f568597d3..128b12bccd5 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/FinalFortune.java +++ b/Mage.Sets/src/mage/sets/seventhedition/FinalFortune.java @@ -28,19 +28,10 @@ package mage.sets.seventhedition; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.turn.TurnMod; -import mage.players.Player; /** * @@ -52,9 +43,8 @@ public class FinalFortune extends CardImpl { super(ownerId, 182, "Final Fortune", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{R}{R}"); this.expansionSetCode = "7ED"; - // Take an extra turn after this one. At the beginning of that turn's end step, you lose the game. - this.getSpellAbility().addEffect(new FinalFortuneEffect()); + this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect(true)); } public FinalFortune(final FinalFortune card) { @@ -66,103 +56,3 @@ public class FinalFortune extends CardImpl { return new FinalFortune(this); } } - -class FinalFortuneEffect extends OneShotEffect { - - public FinalFortuneEffect() { - super(Outcome.AIDontUseIt); - this.staticText = "Take an extra turn after this one. At the beginning of that turn's end step, you lose the game."; - } - - public FinalFortuneEffect(final FinalFortuneEffect effect) { - super(effect); - } - - @Override - public FinalFortuneEffect copy() { - return new FinalFortuneEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - // Take an extra turn after this one - TurnMod extraTurn = new TurnMod(source.getControllerId(), false); - game.getState().getTurnMods().add(extraTurn); - - FinalFortuneLoseDelayedTriggeredAbility delayedTriggeredAbility = new FinalFortuneLoseDelayedTriggeredAbility(); - delayedTriggeredAbility.setSourceId(source.getSourceId()); - delayedTriggeredAbility.setControllerId(source.getControllerId()); - delayedTriggeredAbility.setConnectedTurnMod(extraTurn.getId()); - game.addDelayedTriggeredAbility(delayedTriggeredAbility); - - return true; - } - -} - -class FinalFortuneLoseDelayedTriggeredAbility extends DelayedTriggeredAbility { - - private UUID connectedTurnMod; - - public FinalFortuneLoseDelayedTriggeredAbility() { - super(new FinalFortuneLoseEffect(), Duration.EndOfGame); - } - - public FinalFortuneLoseDelayedTriggeredAbility(final FinalFortuneLoseDelayedTriggeredAbility ability) { - super(ability); - this.connectedTurnMod = ability.connectedTurnMod; - } - - @Override - public FinalFortuneLoseDelayedTriggeredAbility copy() { - return new FinalFortuneLoseDelayedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return connectedTurnMod != null && connectedTurnMod.equals(game.getState().getTurnId()); - } - - public void setConnectedTurnMod(UUID connectedTurnMod) { - this.connectedTurnMod = connectedTurnMod; - } - - @Override - public String getRule() { - return "At the beginning of that turn's end step, you lose the game"; - } - -} - -class FinalFortuneLoseEffect extends OneShotEffect { - - public FinalFortuneLoseEffect() { - super(Outcome.Detriment); - this.staticText = "You lose the game"; - } - - public FinalFortuneLoseEffect(final FinalFortuneLoseEffect effect) { - super(effect); - } - - @Override - public FinalFortuneLoseEffect copy() { - return new FinalFortuneLoseEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.lost(game); - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/starter1999/LastChance.java b/Mage.Sets/src/mage/sets/starter1999/LastChance.java new file mode 100644 index 00000000000..57860b5a28f --- /dev/null +++ b/Mage.Sets/src/mage/sets/starter1999/LastChance.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.starter1999; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class LastChance extends mage.sets.masterseditioniv.LastChance { + + public LastChance(UUID ownerId) { + super(ownerId); + this.cardNumber = 110; + this.expansionSetCode = "S99"; + } + + public LastChance(final LastChance card) { + super(card); + } + + @Override + public LastChance copy() { + return new LastChance(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java b/Mage/src/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java index a1fcdcf0bf7..9b08d1d3618 100644 --- a/Mage/src/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java +++ b/Mage/src/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java @@ -27,10 +27,16 @@ */ package mage.abilities.effects.common.turn; +import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LoseGameSourceControllerEffect; +import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.turn.TurnMod; import mage.players.Player; @@ -39,13 +45,24 @@ import mage.players.Player; */ public class AddExtraTurnControllerEffect extends OneShotEffect { + private final boolean loseGameAtEnd; + public AddExtraTurnControllerEffect() { - super(Outcome.ExtraTurn); + this(false); + } + + public AddExtraTurnControllerEffect(boolean loseGameAtEnd) { + super(loseGameAtEnd ? Outcome.AIDontUseIt : Outcome.ExtraTurn); + this.loseGameAtEnd = loseGameAtEnd; staticText = "Take an extra turn after this one"; + if(loseGameAtEnd) { + staticText += ". At the beginning of that turn's end step, you lose the game"; + } } public AddExtraTurnControllerEffect(final AddExtraTurnControllerEffect effect) { super(effect); + this.loseGameAtEnd = effect.loseGameAtEnd; } @Override @@ -57,9 +74,55 @@ public class AddExtraTurnControllerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.getState().getTurnMods().add(new TurnMod(player.getId(), false)); + TurnMod extraTurn = new TurnMod(player.getId(), false); + game.getState().getTurnMods().add(extraTurn); + if(loseGameAtEnd) { + LoseGameDelayedTriggeredAbility delayedTriggeredAbility = new LoseGameDelayedTriggeredAbility(); + delayedTriggeredAbility.setSourceId(source.getSourceId()); + delayedTriggeredAbility.setControllerId(source.getControllerId()); + delayedTriggeredAbility.setConnectedTurnMod(extraTurn.getId()); + game.addDelayedTriggeredAbility(delayedTriggeredAbility); + } } return true; } -} \ No newline at end of file +} + +class LoseGameDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private UUID connectedTurnMod; + + public LoseGameDelayedTriggeredAbility() { + super(new LoseGameSourceControllerEffect(), Duration.EndOfGame); + } + + public LoseGameDelayedTriggeredAbility(final LoseGameDelayedTriggeredAbility ability) { + super(ability); + this.connectedTurnMod = ability.connectedTurnMod; + } + + @Override + public LoseGameDelayedTriggeredAbility copy() { + return new LoseGameDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.END_TURN_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return connectedTurnMod != null && connectedTurnMod.equals(game.getState().getTurnId()); + } + + public void setConnectedTurnMod(UUID connectedTurnMod) { + this.connectedTurnMod = connectedTurnMod; + } + + @Override + public String getRule() { + return "At the beginning of that turn's end step, you lose the game"; + } +} From 0ca991c4baca4dd17b4a4f4cf3a93aa79c388567 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 19:35:02 +0200 Subject: [PATCH 32/39] * Starfield of Nyx - Fixed that the continuous effect's dependancy to continuousEffects that add subtype aura was not checked. Reworked the dependency check to be a bit more generic (#1259). --- .../sets/elvesvsgoblins/AmbushCommander.java | 14 +- .../src/mage/sets/fatereforged/Rageform.java | 4 - .../mage/sets/futuresight/MagusOfTheMoon.java | 2 + .../mage/sets/limitedalpha/Conversion.java | 4 +- .../mage/sets/limitedalpha/KormusBell.java | 15 +- .../mage/sets/limitedalpha/LivingLands.java | 9 +- .../sets/magicorigins/StarfieldOfNyx.java | 25 ++- .../sets/masterseditionii/TheloniteDruid.java | 9 +- .../sets/shadowmoor/EnchantedEvening.java | 2 + .../mage/sets/urzasdestiny/Opalescence.java | 15 +- .../src/mage/sets/visions/Necromancy.java | 47 +++-- .../enchantments/StarfieldOfNyxTest.java | 81 ++++++++ .../cards/facedown/BaneAlleyBrokerTest.java | 20 +- .../mage/abilities/common/LicidAbility.java | 39 ++-- .../ConditionalContinuousEffect.java | 17 +- .../abilities/effects/ContinuousEffect.java | 3 + .../effects/ContinuousEffectImpl.java | 10 + .../continuous/AddCardTypeTargetEffect.java | 15 +- .../continuous/BecomesAuraSourceEffect.java | 5 +- .../BecomesBasicLandTargetEffect.java | 16 ++ .../mage/abilities/keyword/BestowAbility.java | 191 +++++++++--------- Mage/src/mage/constants/DependencyType.java | 50 +++++ 22 files changed, 408 insertions(+), 185 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java create mode 100644 Mage/src/mage/constants/DependencyType.java diff --git a/Mage.Sets/src/mage/sets/elvesvsgoblins/AmbushCommander.java b/Mage.Sets/src/mage/sets/elvesvsgoblins/AmbushCommander.java index 673340d63a6..dc7ab7e9742 100644 --- a/Mage.Sets/src/mage/sets/elvesvsgoblins/AmbushCommander.java +++ b/Mage.Sets/src/mage/sets/elvesvsgoblins/AmbushCommander.java @@ -34,10 +34,12 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; @@ -71,12 +73,13 @@ public class AmbushCommander extends CardImpl { this.toughness = new MageInt(2); // Forests you control are 1/1 green Elf creatures that are still lands. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesCreatureAllEffect(new AmbushCommanderToken(), - "lands", filter2, Duration.WhileOnBattlefield))); + ContinuousEffect effect = new BecomesCreatureAllEffect(new AmbushCommanderToken(), "lands", filter2, Duration.WhileOnBattlefield); + effect.getDependencyTypes().add(DependencyType.BecomeForest); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // {1}{G}, Sacrifice an Elf: Target creature gets +3/+3 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(3,3, Duration.EndOfTurn), - new ManaCostsImpl("{1}{G}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, filter, true))); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(3, 3, Duration.EndOfTurn), + new ManaCostsImpl("{1}{G}")); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -92,6 +95,7 @@ public class AmbushCommander extends CardImpl { } class AmbushCommanderToken extends Token { + public AmbushCommanderToken() { super("Elf", "1/1 green Elf creatures"); subtype.add("Elf"); diff --git a/Mage.Sets/src/mage/sets/fatereforged/Rageform.java b/Mage.Sets/src/mage/sets/fatereforged/Rageform.java index 1be0e8edf34..ff71e3a7c6e 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/Rageform.java +++ b/Mage.Sets/src/mage/sets/fatereforged/Rageform.java @@ -28,15 +28,11 @@ package mage.sets.fatereforged; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.BecomesAuraAttachToManifestSourceEffect; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.DoubleStrikeAbility; -import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.constants.AttachmentType; import mage.constants.CardType; diff --git a/Mage.Sets/src/mage/sets/futuresight/MagusOfTheMoon.java b/Mage.Sets/src/mage/sets/futuresight/MagusOfTheMoon.java index f8add1d6b61..4d1d52f33ef 100644 --- a/Mage.Sets/src/mage/sets/futuresight/MagusOfTheMoon.java +++ b/Mage.Sets/src/mage/sets/futuresight/MagusOfTheMoon.java @@ -35,6 +35,7 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.mana.RedManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -86,6 +87,7 @@ public class MagusOfTheMoon extends CardImpl { MagusOfTheMoonEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Nonbasic lands are Mountains"; + dependencyTypes.add(DependencyType.BecomeMountain); } MagusOfTheMoonEffect(final MagusOfTheMoonEffect effect) { diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Conversion.java b/Mage.Sets/src/mage/sets/limitedalpha/Conversion.java index fcb7c843371..30cd11eedd8 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/Conversion.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/Conversion.java @@ -41,6 +41,7 @@ import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import static mage.constants.Layer.AbilityAddingRemovingEffects_6; @@ -53,7 +54,6 @@ import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.sets.futuresight.MagusOfTheMoon; /** * @@ -133,7 +133,7 @@ public class Conversion extends CardImpl { Set dependentTo = null; for (ContinuousEffect effect : allEffectsInLayer) { // http://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/286046-conversion-magus-of-the-moon - if (MagusOfTheMoon.class.equals(effect.getClass().getEnclosingClass())) { + if (effect.getDependencyTypes().contains(DependencyType.BecomeMountain)) { if (dependentTo == null) { dependentTo = new HashSet<>(); } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/KormusBell.java b/Mage.Sets/src/mage/sets/limitedalpha/KormusBell.java index f4c50b2b2b8..ddc142d6a5b 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/KormusBell.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/KormusBell.java @@ -29,24 +29,22 @@ package mage.sets.limitedalpha; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.permanent.token.Token; /** * * @author KholdFuzion - + * */ public class KormusBell extends CardImpl { @@ -55,7 +53,9 @@ public class KormusBell extends CardImpl { this.expansionSetCode = "LEA"; // All Swamps are 1/1 black creatures that are still lands. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesCreatureAllEffect(new KormusBellToken(), "lands", new FilterPermanent("Swamp", "Swamps"), Duration.WhileOnBattlefield))); + ContinuousEffect effect = new BecomesCreatureAllEffect(new KormusBellToken(), "lands", new FilterPermanent("Swamp", "Swamps"), Duration.WhileOnBattlefield); + effect.getDependencyTypes().add(DependencyType.BecomeSwamp); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public KormusBell(final KormusBell card) { @@ -69,6 +69,7 @@ public class KormusBell extends CardImpl { } class KormusBellToken extends Token { + public KormusBellToken() { super("", "1/1 creature"); cardType.add(CardType.CREATURE); @@ -77,4 +78,4 @@ class KormusBellToken extends Token { color.setBlack(true); //Check Oracle, yes they are black } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/LivingLands.java b/Mage.Sets/src/mage/sets/limitedalpha/LivingLands.java index 930379988c5..cd4923f3e07 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/LivingLands.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/LivingLands.java @@ -30,9 +30,11 @@ package mage.sets.limitedalpha; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; @@ -51,14 +53,15 @@ public class LivingLands extends CardImpl { static { filter.add(new SubtypePredicate("Forest")); } - + public LivingLands(UUID ownerId) { super(ownerId, 118, "Living Lands", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); this.expansionSetCode = "LEA"; - // All Forests are 1/1 creatures that are still lands. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesCreatureAllEffect(new LivingLandsToken(), "lands", filter, Duration.WhileOnBattlefield))); + ContinuousEffect effect = new BecomesCreatureAllEffect(new LivingLandsToken(), "lands", filter, Duration.WhileOnBattlefield); + effect.getDependencyTypes().add(DependencyType.BecomeForest); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public LivingLands(final LivingLands card) { diff --git a/Mage.Sets/src/mage/sets/magicorigins/StarfieldOfNyx.java b/Mage.Sets/src/mage/sets/magicorigins/StarfieldOfNyx.java index 9b3783f0082..33d8c72673c 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/StarfieldOfNyx.java +++ b/Mage.Sets/src/mage/sets/magicorigins/StarfieldOfNyx.java @@ -27,16 +27,21 @@ */ package mage.sets.magicorigins; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -70,6 +75,7 @@ public class StarfieldOfNyx extends CardImpl { static { filterEnchantmentYouControl.add(new ControllerPredicate(TargetController.YOU)); } + static { filterGraveyardEnchantment.add(new CardTypePredicate(CardType.ENCHANTMENT)); filterGraveyardEnchantment.add(new OwnerPredicate(TargetController.YOU)); @@ -86,7 +92,8 @@ public class StarfieldOfNyx extends CardImpl { this.addAbility(ability); // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in addition to its other types and has base power and base toughness each equal to its converted mana cost. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new StarfieldOfNyxEffect(), new PermanentsOnTheBattlefieldCondition(filterEnchantmentYouControl, PermanentsOnTheBattlefieldCondition.CountType.MORE_THAN, 4), rule1); + ConditionalContinuousEffect effect = new ConditionalContinuousEffect( + new StarfieldOfNyxEffect(), new PermanentsOnTheBattlefieldCondition(filterEnchantmentYouControl, PermanentsOnTheBattlefieldCondition.CountType.MORE_THAN, 4), rule1); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } @@ -103,6 +110,7 @@ public class StarfieldOfNyx extends CardImpl { class StarfieldOfNyxEffect extends ContinuousEffectImpl { private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Each other non-Aura enchantment you control"); + static { filter.add(Predicates.not(new SubtypePredicate("Aura"))); filter.add(new AnotherPredicate()); @@ -152,10 +160,23 @@ class StarfieldOfNyxEffect extends ContinuousEffectImpl { return false; } - @Override public boolean hasLayer(Layer layer) { return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; } + @Override + public Set isDependentTo(List allEffectsInLayer) { + Set dependentTo = null; + for (ContinuousEffect effect : allEffectsInLayer) { + // http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=17664&start=30#p185513 + if (effect.getDependencyTypes().contains(DependencyType.AuraAddingRemoving)) { + if (dependentTo == null) { + dependentTo = new HashSet<>(); + } + dependentTo.add(effect.getId()); + } + } + return dependentTo; + } } diff --git a/Mage.Sets/src/mage/sets/masterseditionii/TheloniteDruid.java b/Mage.Sets/src/mage/sets/masterseditionii/TheloniteDruid.java index 76a2c1bead1..758cc68cfab 100644 --- a/Mage.Sets/src/mage/sets/masterseditionii/TheloniteDruid.java +++ b/Mage.Sets/src/mage/sets/masterseditionii/TheloniteDruid.java @@ -34,9 +34,11 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; @@ -67,8 +69,10 @@ public class TheloniteDruid extends CardImpl { this.toughness = new MageInt(1); // {1}{G}, {tap}, Sacrifice a creature: Forests you control become 2/3 creatures until end of turn. They're still lands. + ContinuousEffect effect = new BecomesCreatureAllEffect(new TheloniteDruidLandToken(), "Forests", filter, Duration.EndOfTurn); + effect.getDependencyTypes().add(DependencyType.BecomeForest); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BecomesCreatureAllEffect(new TheloniteDruidLandToken(), "Forests", filter, Duration.EndOfTurn), + effect, new ManaCostsImpl("{1}{G}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); @@ -86,10 +90,11 @@ public class TheloniteDruid extends CardImpl { } class TheloniteDruidLandToken extends Token { + public TheloniteDruidLandToken() { super("", "2/3 creatures"); cardType.add(CardType.CREATURE); power = new MageInt(2); toughness = new MageInt(3); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/EnchantedEvening.java b/Mage.Sets/src/mage/sets/shadowmoor/EnchantedEvening.java index 9590020f571..5c81a272512 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/EnchantedEvening.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/EnchantedEvening.java @@ -34,6 +34,7 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -80,6 +81,7 @@ public class EnchantedEvening extends CardImpl { super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); this.addedCardType = addedCardType; this.filter = filter; + this.dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); } public EnchangedEveningEffect(final EnchangedEveningEffect effect) { diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Opalescence.java b/Mage.Sets/src/mage/sets/urzasdestiny/Opalescence.java index cd11bb83449..32b44a5f8ea 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/Opalescence.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Opalescence.java @@ -27,6 +27,7 @@ */ package mage.sets.urzasdestiny; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -37,6 +38,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import static mage.constants.Layer.PTChangingEffects_7; @@ -51,7 +53,6 @@ import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.sets.shadowmoor.EnchantedEvening; /** * @@ -82,10 +83,12 @@ public class Opalescence extends CardImpl { class OpalescenceEffect extends ContinuousEffectImpl { private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Each other non-Aura enchantment"); + private static final EnumSet checkDependencyTypes; static { filter.add(Predicates.not(new SubtypePredicate("Aura"))); filter.add(new AnotherPredicate()); + checkDependencyTypes = EnumSet.of(DependencyType.AuraAddingRemoving, DependencyType.EnchantmentAddingRemoving); } public OpalescenceEffect() { @@ -140,11 +143,13 @@ class OpalescenceEffect extends ContinuousEffectImpl { public Set isDependentTo(List allEffectsInLayer) { Set dependentTo = null; for (ContinuousEffect effect : allEffectsInLayer) { - if (EnchantedEvening.class.equals(effect.getClass().getEnclosingClass())) { - if (dependentTo == null) { - dependentTo = new HashSet<>(); + for (DependencyType dependencyType : effect.getDependencyTypes()) { + if (checkDependencyTypes.contains(dependencyType)) { + if (dependentTo == null) { + dependentTo = new HashSet<>(); + } + dependentTo.add(effect.getId()); } - dependentTo.add(effect.getId()); } } return dependentTo; diff --git a/Mage.Sets/src/mage/sets/visions/Necromancy.java b/Mage.Sets/src/mage/sets/visions/Necromancy.java index 0525df83ffc..f109d320e53 100644 --- a/Mage.Sets/src/mage/sets/visions/Necromancy.java +++ b/Mage.Sets/src/mage/sets/visions/Necromancy.java @@ -48,6 +48,7 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.AsThoughEffectType; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -77,13 +78,12 @@ public class Necromancy extends CardImpl { super(ownerId, 14, "Necromancy", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); this.expansionSetCode = "VIS"; - // 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. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastSourceAsThoughItHadFlashEffect(this, Duration.EndOfGame, true))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastSourceAsThoughItHadFlashEffect(this, Duration.EndOfGame, true))); this.addAbility(new CastAtInstantTimeTriggeredAbility()); - - // 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 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. Ability ability = new ConditionalTriggeredAbility( new EntersBattlefieldTriggeredAbility(new NecromancyReAttachEffect(), false), @@ -91,7 +91,7 @@ public class Necromancy extends CardImpl { "When {this} enters the battlefield, if it's on the battlefield, it becomes an Aura with \"enchant creature put onto the battlefield with {this}.\" Put target creature card from a graveyard onto the battlefield under your control and attach {this} to it."); ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); this.addAbility(ability); - this.addAbility(new LeavesBattlefieldTriggeredAbility(new NecromancyLeavesBattlefieldTriggeredEffect(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new NecromancyLeavesBattlefieldTriggeredEffect(), false)); } public Necromancy(final Necromancy card) { @@ -104,7 +104,6 @@ public class Necromancy extends CardImpl { } } - class CastSourceAsThoughItHadFlashEffect extends AsThoughEffectImpl { private final boolean sacrificeIfCastAsInstant; @@ -115,7 +114,6 @@ class CastSourceAsThoughItHadFlashEffect extends AsThoughEffectImpl { staticText = "You may cast {this} as though it had flash"; } - public CastSourceAsThoughItHadFlashEffect(final CastSourceAsThoughItHadFlashEffect effect) { super(effect); this.sacrificeIfCastAsInstant = effect.sacrificeIfCastAsInstant; @@ -139,6 +137,7 @@ class CastSourceAsThoughItHadFlashEffect extends AsThoughEffectImpl { } class CastAtInstantTimeTriggeredAbility extends TriggeredAbilityImpl { + public CastAtInstantTimeTriggeredAbility() { super(Zone.STACK, new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextCleanupDelayedTriggeredAbility(new SacrificeSourceEffect()))); } @@ -176,21 +175,21 @@ class CastAtInstantTimeTriggeredAbility extends TriggeredAbilityImpl { } class NecromancyReAttachEffect extends OneShotEffect { - + public NecromancyReAttachEffect() { super(Outcome.Benefit); this.staticText = "it becomes an Aura with \"enchant creature put onto the battlefield with {this}\""; } - + public NecromancyReAttachEffect(final NecromancyReAttachEffect effect) { super(effect); } - + @Override public NecromancyReAttachEffect copy() { return new NecromancyReAttachEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -211,27 +210,27 @@ class NecromancyReAttachEffect extends OneShotEffect { } return true; } - + return false; } } - + class NecromancyLeavesBattlefieldTriggeredEffect extends OneShotEffect { - + public NecromancyLeavesBattlefieldTriggeredEffect() { super(Outcome.Benefit); this.staticText = "enchanted creature's controller sacrifices it"; } - + public NecromancyLeavesBattlefieldTriggeredEffect(final NecromancyLeavesBattlefieldTriggeredEffect effect) { super(effect); } - + @Override public NecromancyLeavesBattlefieldTriggeredEffect copy() { return new NecromancyLeavesBattlefieldTriggeredEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -252,7 +251,7 @@ class NecromancyLeavesBattlefieldTriggeredEffect extends OneShotEffect { class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements SourceEffect { private final static Ability newAbility = new EnchantAbility("creature put onto the battlefield with Necromancy"); - + static { newAbility.setRuleAtTheTop(true); } @@ -263,9 +262,9 @@ class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements Sour super(Duration.Custom, Outcome.AddAbility); staticText = "it becomes an Aura with \"enchant creature put onto the battlefield with {this}\""; this.target = target; + dependencyTypes.add(DependencyType.AuraAddingRemoving); } - public NecromancyChangeAbilityEffect(final NecromancyChangeAbilityEffect effect) { super(effect); this.target = effect.target; @@ -275,7 +274,7 @@ class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements Sour public NecromancyChangeAbilityEffect copy() { return new NecromancyChangeAbilityEffect(this); } - + @Override public void init(Ability source, Game game) { super.init(source, game); @@ -301,8 +300,8 @@ class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements Sour permanent.getSpellAbility().getTargets().add(target); } } - return true; - } + return true; + } this.discard(); return false; } @@ -316,5 +315,5 @@ class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements Sour public boolean hasLayer(Layer layer) { return Layer.AbilityAddingRemovingEffects_6.equals(layer) || Layer.TypeChangingEffects_4.equals(layer); } - + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java new file mode 100644 index 00000000000..e87ccda6f73 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.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 org.mage.test.cards.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class StarfieldOfNyxTest extends CardTestPlayerBase { + + /** + * I had Starfield of Nyx out. With the upkeep trigger, I brought back a + * Cloudform, which was the fifth enchantment. I had another Cloudform, and + * Starfield of Nyx not only turned both of them into creatures (it + * shouldn't, because they're auras), but it also destroyed them. The + * manifests stayed on the battlefield without Flying or Hexproof. + * + */ + @Test + public void testBaneAlleyBroker() { + // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. + // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Thopter Spy Network", 2); // {2}{U}{U} - added to come to 5 enchantments on the battlefield + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + // At the beginning of your upkeep, you may return target enchantment card from your graveyard to the battlefield. + // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in + // addition to its other types and has base power and base toughness each equal to its converted mana cost. + addCard(Zone.HAND, playerA, "Starfield of Nyx"); // "{4}{W}" + // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. Manifest the top card of your library and attach Cloudform to it. + // Enchanted creature has flying and hexproof. + addCard(Zone.HAND, playerA, "Cloudform"); // {1}{U}{U} + addCard(Zone.GRAVEYARD, playerA, "Cloudform"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Starfield of Nyx"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudform"); + + addTarget(playerA, "Cloudform"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Thopter Spy Network", 0); + assertPowerToughness(playerA, "", 2, 2, Filter.ComparisonScope.All); // the manifested cards + assertPermanentCount(playerA, "Starfield of Nyx", 1); + assertPowerToughness(playerA, "Thopter Spy Network", 4, 4, Filter.ComparisonScope.All); + assertPermanentCount(playerA, "Cloudform", 2); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java index 025c46e4f3f..d8f254f899e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/BaneAlleyBrokerTest.java @@ -3,9 +3,7 @@ package org.mage.test.cards.facedown; import mage.cards.Card; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.game.ExileZone; import org.junit.Assert; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -15,14 +13,12 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class BaneAlleyBrokerTest extends CardTestPlayerBase { /** - * Bane Alley Broker - * Creature — Human Rogue 0/3, 1UB (3) - * {T}: Draw a card, then exile a card from your hand face down. - * You may look at cards exiled with Bane Alley Broker. - * {U}{B}, {T}: Return a card exiled with Bane Alley Broker to its owner's hand. - * + * Bane Alley Broker Creature — Human Rogue 0/3, 1UB (3) {T}: Draw a card, + * then exile a card from your hand face down. You may look at cards exiled + * with Bane Alley Broker. {U}{B}, {T}: Return a card exiled with Bane Alley + * Broker to its owner's hand. + * */ - // test that cards exiled using Bane Alley Broker are face down @Test public void testBaneAlleyBroker() { @@ -31,20 +27,20 @@ public class BaneAlleyBrokerTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Sejiri Merfolk"); addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.BATTLEFIELD, playerA, "Swamp"); - + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then exile a card from your hand face down."); addTarget(playerA, "Goblin Roughrider"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + assertHandCount(playerA, 2); assertHandCount(playerA, "Sejiri Merfolk", 1); assertHandCount(playerA, "Goblin Roughrider", 0); assertExileCount("Goblin Roughrider", 1); - for (Card card :currentGame.getExile().getAllCards(currentGame)){ + for (Card card : currentGame.getExile().getAllCards(currentGame)) { if (card.getName().equals("Goblin Roughrider")) { Assert.assertTrue("Exiled card is not face down", card.isFaceDown(currentGame)); } diff --git a/Mage/src/mage/abilities/common/LicidAbility.java b/Mage/src/mage/abilities/common/LicidAbility.java index a321266836d..bdc12b0b6cf 100644 --- a/Mage/src/mage/abilities/common/LicidAbility.java +++ b/Mage/src/mage/abilities/common/LicidAbility.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.common; import java.util.UUID; @@ -42,6 +41,7 @@ import mage.abilities.effects.common.CreateSpecialActionEffect; import mage.abilities.effects.common.RemoveSpecialActionEffect; import mage.abilities.keyword.EnchantAbility; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -76,25 +76,25 @@ public class LicidAbility extends ActivatedAbilityImpl { } class LicidEffect extends OneShotEffect { - + private final ManaCost specialActionCost; - + LicidEffect(ManaCost specialActionCost) { super(Outcome.Neutral); this.specialActionCost = specialActionCost; this.staticText = "{this} loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay " + specialActionCost.getText() + " to end this effect"; } - + LicidEffect(final LicidEffect effect) { super(effect); this.specialActionCost = effect.specialActionCost; } - + @Override public LicidEffect copy() { return new LicidEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Permanent licid = (Permanent) source.getSourceObjectIfItStillExists(game); @@ -113,15 +113,16 @@ class LicidEffect extends OneShotEffect { } class LicidContinuousEffect extends ContinuousEffectImpl { - + private final UUID messageId; - + LicidContinuousEffect(UUID messageId) { super(Duration.Custom, Outcome.Neutral); this.messageId = messageId; + dependencyTypes.add(DependencyType.AuraAddingRemoving); } - LicidContinuousEffect(LicidContinuousEffect ability) { + LicidContinuousEffect(final LicidContinuousEffect ability) { super(ability); this.messageId = ability.messageId; } @@ -201,28 +202,28 @@ class LicidSpecialAction extends SpecialAction { } class LicidSpecialActionEffect extends OneShotEffect { - + private final UUID messageId; private final UUID generatingSpecialActionId; - + LicidSpecialActionEffect(UUID messageId, UUID generatingSpecialActionId, String licidName) { super(Outcome.Neutral); this.messageId = messageId; this.generatingSpecialActionId = generatingSpecialActionId; this.staticText = "End " + licidName + " Effect"; } - + LicidSpecialActionEffect(final LicidSpecialActionEffect effect) { super(effect); this.messageId = effect.messageId; this.generatingSpecialActionId = effect.generatingSpecialActionId; } - + @Override public LicidSpecialActionEffect copy() { return new LicidSpecialActionEffect(this); } - + @Override public boolean apply(Game game, Ability source) { new RemoveSpecialActionEffect(this.generatingSpecialActionId).apply(game, source); diff --git a/Mage/src/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/mage/abilities/decorator/ConditionalContinuousEffect.java index 7ea23fd4443..fb7f004e162 100644 --- a/Mage/src/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -1,5 +1,8 @@ package mage.abilities.decorator; +import java.util.List; +import java.util.Set; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.Condition; @@ -25,7 +28,6 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { protected Condition condition; protected boolean initDone = false; - public ConditionalContinuousEffect(ContinuousEffect effect, Condition condition, String text) { this(effect, null, condition, text); } @@ -61,7 +63,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { public boolean isDiscarded() { return this.discarded || effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded()); } - + @Override public void init(Ability source, Game game) { if (baseCondition instanceof LockedInCondition) { @@ -70,7 +72,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { condition = baseCondition; } effect.setTargetPointer(this.targetPointer); - effect.init(source, game); + effect.init(source, game); if (otherwiseEffect != null) { otherwiseEffect.setTargetPointer(this.targetPointer); otherwiseEffect.init(source, game); @@ -134,4 +136,13 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { public ConditionalContinuousEffect copy() { return new ConditionalContinuousEffect(this); } + + @Override + public Set isDependentTo(List allEffectsInLayer) { + if (effect != null) { + return effect.isDependentTo(allEffectsInLayer); + } + return super.isDependentTo(allEffectsInLayer); + } + } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffect.java b/Mage/src/mage/abilities/effects/ContinuousEffect.java index 355ff4347a5..0e3635eeea9 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffect.java @@ -32,6 +32,7 @@ import java.util.Set; import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.SubLayer; @@ -73,6 +74,8 @@ public interface ContinuousEffect extends Effect { Set isDependentTo(List allEffectsInLayer); + Set getDependencyTypes(); + @Override void newId(); diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java index 48bd0bf8a27..0d2a81670be 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java @@ -28,6 +28,7 @@ package mage.abilities.effects; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -39,6 +40,7 @@ import mage.abilities.dynamicvalue.common.DomainValue; import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.constants.AbilityType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.EffectType; import mage.constants.Layer; @@ -69,6 +71,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu protected boolean affectedObjectsSet = false; protected List affectedObjectList = new ArrayList<>(); protected boolean temporary = false; + protected EnumSet dependencyTypes; // until your next turn protected int startingTurn; @@ -79,6 +82,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.duration = duration; this.order = 0; this.effectType = EffectType.CONTINUOUS; + this.dependencyTypes = EnumSet.noneOf(DependencyType.class); } public ContinuousEffectImpl(Duration duration, Layer layer, SubLayer sublayer, Outcome outcome) { @@ -100,6 +104,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.temporary = effect.temporary; this.startingTurn = effect.startingTurn; this.startingControllerId = effect.startingControllerId; + this.dependencyTypes = effect.dependencyTypes; } @Override @@ -256,4 +261,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu return null; } + @Override + public EnumSet getDependencyTypes() { + return dependencyTypes; + } + } diff --git a/Mage/src/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java index 2c51a470c00..19ce641041f 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/AddCardTypeTargetEffect.java @@ -25,14 +25,18 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.continuous; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; @@ -40,12 +44,15 @@ import mage.game.permanent.Permanent; * @author nantuko */ public class AddCardTypeTargetEffect extends ContinuousEffectImpl { - + private final CardType addedCardType; public AddCardTypeTargetEffect(CardType addedCardType, Duration duration) { super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); this.addedCardType = addedCardType; + if (addedCardType.equals(CardType.ENCHANTMENT)) { + dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); + } } public AddCardTypeTargetEffect(final AddCardTypeTargetEffect effect) { @@ -56,7 +63,7 @@ public class AddCardTypeTargetEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { boolean result = false; - for (UUID targetId :targetPointer.getTargets(game, source)) { + for (UUID targetId : targetPointer.getTargets(game, source)) { Permanent target = game.getPermanent(targetId); if (target != null) { if (!target.getCardType().contains(addedCardType)) { diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java index e0f07a65544..844447ddd7a 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesAuraSourceEffect.java @@ -31,6 +31,7 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.EnchantAbility; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import static mage.constants.Layer.AbilityAddingRemovingEffects_6; @@ -56,10 +57,10 @@ public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements Sou newAbility = new EnchantAbility(target.getTargetName()); newAbility.setRuleAtTheTop(true); staticText = "it becomes an Aura with enchant " + target.getTargetName(); + dependencyTypes.add(DependencyType.AuraAddingRemoving); } - public BecomesAuraSourceEffect(final BecomesAuraSourceEffect effect) { super(effect); this.target = effect.target; @@ -112,4 +113,4 @@ public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements Sou return Layer.AbilityAddingRemovingEffects_6.equals(layer) || Layer.TypeChangingEffects_4.equals(layer); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index 44973fb990c..43e1bf12aad 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java @@ -40,6 +40,7 @@ import mage.abilities.mana.WhiteManaAbility; import mage.choices.Choice; import mage.choices.ChoiceBasicLandType; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -73,6 +74,21 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { public BecomesBasicLandTargetEffect(Duration duration, boolean chooseLandType, boolean loseOther, String... landNames) { super(duration, Outcome.Detriment); this.landTypes.addAll(Arrays.asList(landNames)); + if (landTypes.contains("Mountain")) { + dependencyTypes.add(DependencyType.BecomeMountain); + } + if (landTypes.contains("Forest")) { + dependencyTypes.add(DependencyType.BecomeForest); + } + if (landTypes.contains("Swamp")) { + dependencyTypes.add(DependencyType.BecomeSwamp); + } + if (landTypes.contains("Island")) { + dependencyTypes.add(DependencyType.BecomeIsland); + } + if (landTypes.contains("Plains")) { + dependencyTypes.add(DependencyType.BecomePlains); + } this.chooseLandType = chooseLandType; this.staticText = setText(); this.loseOther = loseOther; diff --git a/Mage/src/mage/abilities/keyword/BestowAbility.java b/Mage/src/mage/abilities/keyword/BestowAbility.java index dd79c1c121f..3f721f87006 100644 --- a/Mage/src/mage/abilities/keyword/BestowAbility.java +++ b/Mage/src/mage/abilities/keyword/BestowAbility.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 mage.abilities.keyword; import mage.abilities.Ability; @@ -37,6 +36,7 @@ import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.SourceEffect; import mage.cards.Card; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; import static mage.constants.Layer.TypeChangingEffects_4; @@ -54,57 +54,66 @@ import mage.target.common.TargetCreaturePermanent; * * 702.102. Bestow * - * 702.102a Bestow represents two static abilities, one that functions while the card with bestow is - * on the stack and another that functions both while it’s on stack and while it’s on the battlefield. - * “Bestow [cost]” means “You may cast this card by paying [cost] rather than its mana cost.” and “If - * you chose to pay this spell’s bestow cost, it becomes an Aura enchantment and gains enchant creature. - * These effects last until one of two things happens: this spell has an illegal target as it resolves - * and or the permanent this spell becomes, becomes unattached.” Paying a card’s bestow cost follows the - * rules for paying alternative costs in rules 601.2b and 601.2e–g. + * 702.102a Bestow represents two static abilities, one that functions while the + * card with bestow is on the stack and another that functions both while it’s + * on stack and while it’s on the battlefield. “Bestow [cost]” means “You may + * cast this card by paying [cost] rather than its mana cost.” and “If you chose + * to pay this spell’s bestow cost, it becomes an Aura enchantment and gains + * enchant creature. These effects last until one of two things happens: this + * spell has an illegal target as it resolves and or the permanent this spell + * becomes, becomes unattached.” Paying a card’s bestow cost follows the rules + * for paying alternative costs in rules 601.2b and 601.2e–g. * - * 702.102b If a spell’s controller chooses to pay its bestow cost, that player chooses a legal target - * for that Aura spell as defined by its enchant creature ability and rule 601.2c. See also rule 303.4. + * 702.102b If a spell’s controller chooses to pay its bestow cost, that player + * chooses a legal target for that Aura spell as defined by its enchant creature + * ability and rule 601.2c. See also rule 303.4. * - * 702.102c A spell’s controller can’t choose to pay its bestow cost unless that player can choose a legal - * target for that spell after it becomes an Aura spell. + * 702.102c A spell’s controller can’t choose to pay its bestow cost unless that + * player can choose a legal target for that spell after it becomes an Aura + * spell. * - * 702.102d As an Aura spell with bestow begins resolving, if its target is illegal, the effect making - * it an Aura spell ends. It continues resolving as a creature spell and will be put onto the battlefield - * under the control of the spell’s controller. This is an exception to rule 608.3a. + * 702.102d As an Aura spell with bestow begins resolving, if its target is + * illegal, the effect making it an Aura spell ends. It continues resolving as a + * creature spell and will be put onto the battlefield under the control of the + * spell’s controller. This is an exception to rule 608.3a. * - * 702.102e If an Aura with bestow is attached to an illegal object or player, it becomes unattached. - * This is an exception to rule 704.5n. - * - * You don’t choose whether the spell is going to be an Aura spell or not until the spell is already on - * the stack. Abilities that affect when you can cast a spell, such as flash, will apply to the creature - * card in whatever zone you’re casting it from. For example, an effect that said you can cast creature - * spells as though they have flash will allow you to cast a creature card with bestow as an Aura spell - * anytime you could cast an instant. + * 702.102e If an Aura with bestow is attached to an illegal object or player, + * it becomes unattached. This is an exception to rule 704.5n. * - * On the stack, a spell with bestow is either a creature spell or an Aura spell. It’s never both. + * You don’t choose whether the spell is going to be an Aura spell or not until + * the spell is already on the stack. Abilities that affect when you can cast a + * spell, such as flash, will apply to the creature card in whatever zone you’re + * casting it from. For example, an effect that said you can cast creature + * spells as though they have flash will allow you to cast a creature card with + * bestow as an Aura spell anytime you could cast an instant. * - * Unlike other Aura spells, an Aura spell with bestow isn’t countered if its target is illegal as it - * begins to resolve. Rather, the effect making it an Aura spell ends, it loses enchant creature, it - * returns to being an enchantment creature spell, and it resolves and enters the battlefield as an + * On the stack, a spell with bestow is either a creature spell or an Aura + * spell. It’s never both. + * + * Unlike other Aura spells, an Aura spell with bestow isn’t countered if its + * target is illegal as it begins to resolve. Rather, the effect making it an + * Aura spell ends, it loses enchant creature, it returns to being an + * enchantment creature spell, and it resolves and enters the battlefield as an * enchantment creature. * - * Unlike other Auras, an Aura with bestow isn’t put into its owner’s graveyard if it becomes unattached. - * Rather, the effect making it an Aura ends, it loses enchant creature, and it remains on the - * battlefield as an enchantment creature. It can attack (and its {T} abilities can be activated, - * if it has any) on the turn it becomes unattached if it’s been under your control continuously, - * even as an Aura, since your most recent turn began. + * Unlike other Auras, an Aura with bestow isn’t put into its owner’s graveyard + * if it becomes unattached. Rather, the effect making it an Aura ends, it loses + * enchant creature, and it remains on the battlefield as an enchantment + * creature. It can attack (and its {T} abilities can be activated, if it has + * any) on the turn it becomes unattached if it’s been under your control + * continuously, even as an Aura, since your most recent turn began. * - * If a permanent with bestow enters the battlefield by any method other than being cast, it will be - * an enchantment creature. You can’t choose to pay the bestow cost and have it become an Aura. + * If a permanent with bestow enters the battlefield by any method other than + * being cast, it will be an enchantment creature. You can’t choose to pay the + * bestow cost and have it become an Aura. * - * Auras attached to a creature don’t become tapped when the creature becomes tapped. Except in some - * rare cases, an Aura with bestow remains untapped when it becomes unattached and becomes a creature. + * Auras attached to a creature don’t become tapped when the creature becomes + * tapped. Except in some rare cases, an Aura with bestow remains untapped when + * it becomes unattached and becomes a creature. * * * @author LevelX2 */ - - public class BestowAbility extends SpellAbility { public BestowAbility(Card card, String manaString) { @@ -135,67 +144,67 @@ public class BestowAbility extends SpellAbility { @Override public String getRule() { - return "Bestow " + getManaCostsToPay().getText()+ " (If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.)"; + return "Bestow " + getManaCostsToPay().getText() + " (If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.)"; } -} + class BestowTypeChangingEffect extends ContinuousEffectImpl implements SourceEffect { -class BestowTypeChangingEffect extends ContinuousEffectImpl implements SourceEffect { + private boolean wasAttached; - private boolean wasAttached; - - public BestowTypeChangingEffect() { - super(Duration.WhileOnBattlefield, Outcome.BoostCreature); - wasAttached = false; - } - - public BestowTypeChangingEffect(final BestowTypeChangingEffect effect) { - super(effect); - this.wasAttached = effect.wasAttached; - } - - @Override - public BestowTypeChangingEffect copy() { - return new BestowTypeChangingEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (permanent.getAttachedTo() == null) { - if (wasAttached && permanent.getSubtype().contains("Aura")) { - permanent.getSubtype().remove("Aura"); - wasAttached = false; - } - } else { - permanent.getCardType().remove(CardType.CREATURE); - permanent.getSubtype().clear(); - if (!permanent.getSubtype().contains("Aura")) { - permanent.getSubtype().add("Aura"); - } - wasAttached = true; - } - } - break; - } - return true; + public BestowTypeChangingEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature); + wasAttached = false; + dependencyTypes.add(DependencyType.AuraAddingRemoving); } - return false; - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + public BestowTypeChangingEffect(final BestowTypeChangingEffect effect) { + super(effect); + this.wasAttached = effect.wasAttached; + } + @Override + public BestowTypeChangingEffect copy() { + return new BestowTypeChangingEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + if (permanent.getAttachedTo() == null) { + if (wasAttached && permanent.getSubtype().contains("Aura")) { + permanent.getSubtype().remove("Aura"); + wasAttached = false; + } + } else { + permanent.getCardType().remove(CardType.CREATURE); + permanent.getSubtype().clear(); + if (!permanent.getSubtype().contains("Aura")) { + permanent.getSubtype().add("Aura"); + } + wasAttached = true; + } + } + break; + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; } } diff --git a/Mage/src/mage/constants/DependencyType.java b/Mage/src/mage/constants/DependencyType.java new file mode 100644 index 00000000000..bae316c5407 --- /dev/null +++ b/Mage/src/mage/constants/DependencyType.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.constants; + +/** + * Dependency types are a part of a workaround solution to handle dependencies + * of continuous effects. + * + * http://magiccards.info/rule/613-interaction-of-continuous-effects.html + * + * https://github.com/magefree/mage/issues/1259 + * + * + * @author LevelX2 + */ +public enum DependencyType { + + AuraAddingRemoving, + BecomeForest, + BecomeIsland, + BecomeMountain, + BecomePlains, + BecomeSwamp, + EnchantmentAddingRemoving; +} From 1c0041f132ec253dab8c063d564effb6ba671b47 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 12 Sep 2015 20:56:44 +0300 Subject: [PATCH 33/39] Implement cards: Bogardan Lancer, Char-Rumbler, Cryptic Annelid, and Knight of Sursi --- .../mage/sets/futuresight/BogardanLancer.java | 66 +++++++++++++++++ .../mage/sets/futuresight/CharRumbler.java | 69 ++++++++++++++++++ .../mage/sets/futuresight/CrypticAnnelid.java | 73 +++++++++++++++++++ .../mage/sets/futuresight/KnightOfSursi.java | 70 ++++++++++++++++++ .../sets/venservskoth/CrypticAnnelid.java | 52 +++++++++++++ 5 files changed, 330 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/futuresight/BogardanLancer.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/CharRumbler.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/CrypticAnnelid.java create mode 100644 Mage.Sets/src/mage/sets/futuresight/KnightOfSursi.java create mode 100644 Mage.Sets/src/mage/sets/venservskoth/CrypticAnnelid.java diff --git a/Mage.Sets/src/mage/sets/futuresight/BogardanLancer.java b/Mage.Sets/src/mage/sets/futuresight/BogardanLancer.java new file mode 100644 index 00000000000..472d2de41fb --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/BogardanLancer.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.keyword.BloodthirstAbility; +import mage.abilities.keyword.FlankingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class BogardanLancer extends CardImpl { + + public BogardanLancer(UUID ownerId) { + super(ownerId, 95, "Bogardan Lancer", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Human"); + this.subtype.add("Knight"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Bloodthirst 1 + this.addAbility(new BloodthirstAbility(1)); + // Flanking + this.addAbility(new FlankingAbility()); + } + + public BogardanLancer(final BogardanLancer card) { + super(card); + } + + @Override + public BogardanLancer copy() { + return new BogardanLancer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/CharRumbler.java b/Mage.Sets/src/mage/sets/futuresight/CharRumbler.java new file mode 100644 index 00000000000..3fa00c13b82 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/CharRumbler.java @@ -0,0 +1,69 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class CharRumbler extends CardImpl { + + public CharRumbler(UUID ownerId) { + super(ownerId, 96, "Char-Rumbler", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Elemental"); + this.power = new MageInt(-1); + this.toughness = new MageInt(3); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + // {R}: Char-Rumbler gets +1/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); + } + + public CharRumbler(final CharRumbler card) { + super(card); + } + + @Override + public CharRumbler copy() { + return new CharRumbler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/CrypticAnnelid.java b/Mage.Sets/src/mage/sets/futuresight/CrypticAnnelid.java new file mode 100644 index 00000000000..3fceca5542d --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/CrypticAnnelid.java @@ -0,0 +1,73 @@ +/* + * 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.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class CrypticAnnelid extends CardImpl { + + public CrypticAnnelid(UUID ownerId) { + super(ownerId, 34, "Cryptic Annelid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Worm"); + this.subtype.add("Beast"); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // When Cryptic Annelid enters the battlefield, scry 1, then scry 2, then scry 3. + Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(1)); + Effect effect = new ScryEffect(2); + effect.setText(", then scry 2"); + ability.addEffect(effect); + effect = new ScryEffect(3); + effect.setText(", then scry 3"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public CrypticAnnelid(final CrypticAnnelid card) { + super(card); + } + + @Override + public CrypticAnnelid copy() { + return new CrypticAnnelid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/KnightOfSursi.java b/Mage.Sets/src/mage/sets/futuresight/KnightOfSursi.java new file mode 100644 index 00000000000..8bb3b138f36 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/KnightOfSursi.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.futuresight; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.FlankingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class KnightOfSursi extends CardImpl { + + public KnightOfSursi(UUID ownerId) { + super(ownerId, 10, "Knight of Sursi", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.expansionSetCode = "FUT"; + this.subtype.add("Human"); + this.subtype.add("Knight"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // flanking + this.addAbility(new FlankingAbility()); + // Suspend 3-{W} + this.addAbility(new SuspendAbility(3, new ManaCostsImpl("{W}"), this)); + } + + public KnightOfSursi(final KnightOfSursi card) { + super(card); + } + + @Override + public KnightOfSursi copy() { + return new KnightOfSursi(this); + } +} diff --git a/Mage.Sets/src/mage/sets/venservskoth/CrypticAnnelid.java b/Mage.Sets/src/mage/sets/venservskoth/CrypticAnnelid.java new file mode 100644 index 00000000000..66d55fade06 --- /dev/null +++ b/Mage.Sets/src/mage/sets/venservskoth/CrypticAnnelid.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.venservskoth; + +import java.util.UUID; + +/** + * + * @author LoneFox + */ +public class CrypticAnnelid extends mage.sets.futuresight.CrypticAnnelid { + + public CrypticAnnelid(UUID ownerId) { + super(ownerId); + this.cardNumber = 15; + this.expansionSetCode = "DDI"; + } + + public CrypticAnnelid(final CrypticAnnelid card) { + super(card); + } + + @Override + public CrypticAnnelid copy() { + return new CrypticAnnelid(this); + } +} From 88c18bcd8c67a6809ff36f0e9e2a133a3ef17ea9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 20:14:44 +0200 Subject: [PATCH 34/39] * Infernal Scarring - Fixed that the "Dies" trigger did not work if the enchanted creature was sacrificed (e.g. using Nantuko Husk). --- .../enchantments/InfernalScarringTest.java | 71 +++++++++++++++++++ .../continuous/GainAbilityAttachedEffect.java | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/enchantments/InfernalScarringTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/InfernalScarringTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/InfernalScarringTest.java new file mode 100644 index 00000000000..af3806772ed --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/InfernalScarringTest.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 org.mage.test.cards.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class InfernalScarringTest extends CardTestPlayerBase { + + /** + * The 'draw a card, when this creature dies' didn't trigger after i + * sacrificed a creature enchanted with infernal scarring (enchanted + * creature was Fetid Imp and the sac trigger came from a Nantuko Husk). + * + */ + @Test + public void testDiesTrigger() { + // Sacrifice a creature: Nantuko Husk gets +2/+2 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Nantuko Husk", 1); // {2}{B} + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Enchant creature + // Enchanted creature gets +2/+0 and has "When this creature dies, draw a card." + addCard(Zone.HAND, playerA, "Infernal Scarring"); // {1}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infernal Scarring", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice a creature"); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Nantuko Husk", 4, 4); + assertGraveyardCount(playerA, "Infernal Scarring", 1); + assertHandCount(playerA, 1); + } + +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 9120ccb5512..d26eaf8ab78 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -113,7 +113,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { } else { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { - permanent = game.getPermanent(equipment.getAttachedTo()); + permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo()); } } if (permanent != null) { From b29fe86aa09f9f0ae8fdc980688ca23b3c172829 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Sep 2015 20:37:24 +0200 Subject: [PATCH 35/39] Added a test. --- .../enchantments/StarfieldOfNyxTest.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java index e87ccda6f73..25921da3b33 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java @@ -30,6 +30,8 @@ package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.filter.Filter; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -48,7 +50,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { * */ @Test - public void testBaneAlleyBroker() { + public void testCloudform() { // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card. addCard(Zone.BATTLEFIELD, playerA, "Thopter Spy Network", 2); // {2}{U}{U} - added to come to 5 enchantments on the battlefield @@ -78,4 +80,46 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Cloudform", 2); } + /** + * Regarding Starfield of Nyx: I tried to bring back a Singing Bell Strike + * to enchant Silumgar, the Drifting Death. I chose the Aura, but instead of + * enchanting Silumgar, it stayed in the graveyard. This shouldn't be the + * case, because Auras only target when they're cast as spells. + */ + @Test + public void testHexproof() { + // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. + // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // At the beginning of your upkeep, you may return target enchantment card from your graveyard to the battlefield. + // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in + // addition to its other types and has base power and base toughness each equal to its converted mana cost. + addCard(Zone.HAND, playerA, "Starfield of Nyx"); // "{4}{W}" + // Enchant creature + // When Singing Bell Strike enters the battlefield, tap enchanted creature. + // Enchanted creature doesn't untap during its controller's untap step. + // Enchanted creature has "{6}: Untap this creature." + addCard(Zone.GRAVEYARD, playerA, "Singing Bell Strike"); + + addCard(Zone.BATTLEFIELD, playerB, "Silumgar, the Drifting Death", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Starfield of Nyx"); + + addTarget(playerA, "Singing Bell Strike"); + setChoice(playerA, "Silumgar, the Drifting Death"); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Starfield of Nyx", 1); + assertPermanentCount(playerA, "Singing Bell Strike", 1); + + Permanent enchantment = getPermanent("Singing Bell Strike", playerA); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Permanent enchanted = currentGame.getPermanent(enchantment.getAttachedTo()); + Assert.assertEquals("Silumgar was enchanted", enchanted.getName().equals("Silumgar, the Drifting Death"), true); + } else { + Assert.assertEquals("Singing Bell Strike not on the battlefield", false, true); + } + } } From 7639f1bc6d63df58f7127d71366adcf0a67590bb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 13 Sep 2015 07:55:04 +0200 Subject: [PATCH 36/39] * Fixed possible NPE and added warning to log to check what abilities have no sourceId (fixes #1261). --- .../abilities/effects/ContinuousEffects.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 8871ea4658f..29bf3662dac 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -1225,13 +1225,17 @@ public class ContinuousEffects implements Serializable { for (ReplacementEffect effect : replacementEffects) { HashSet abilities = replacementEffects.getAbility(effect.getId()); for (Ability ability : abilities) { - if (ability.getSourceId().equals(sourceId)) { - if (controllerFound == null || controllerFound == ability.getControllerId()) { - controllerFound = ability.getControllerId(); - } else { - // not unique controller - No solution yet - return null; + if (ability.getSourceId() != null) { + if (ability.getSourceId().equals(sourceId)) { + if (controllerFound == null || controllerFound == ability.getControllerId()) { + controllerFound = ability.getControllerId(); + } else { + // not unique controller - No solution yet + return null; + } } + } else { + logger.warn("Ability without sourceId:" + ability.getRule()); } } } From 63e25cf13247ee45ee17247d1a61dcc3bc194b76 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 13 Sep 2015 08:47:45 +0200 Subject: [PATCH 37/39] * Shallow Grave - Fixed that the delayed triggered ability did also effect the target if it changed zone meanwhile (fixes #1262). --- .../src/mage/sets/mirage/ShallowGrave.java | 45 ++++++------- .../cards/abilities/keywords/PersistTest.java | 2 +- .../effects/common/ExileTargetEffect.java | 63 ++++++++++--------- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java b/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java index b2e00d17bb5..67ef2954399 100644 --- a/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java +++ b/Mage.Sets/src/mage/sets/mirage/ShallowGrave.java @@ -44,6 +44,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -57,10 +58,9 @@ public class ShallowGrave extends CardImpl { super(ownerId, 39, "Shallow Grave", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "MIR"; - // Return the top creature card of your graveyard to the battlefield. That creature gains haste until end of turn. Exile it at the beginning of the next end step. this.getSpellAbility().addEffect(new ShallowGraveEffect()); - + } public ShallowGrave(final ShallowGrave card) { @@ -74,46 +74,49 @@ public class ShallowGrave extends CardImpl { } class ShallowGraveEffect extends OneShotEffect { - + public ShallowGraveEffect() { super(Outcome.Benefit); this.staticText = "Return the top creature card of your graveyard to the battlefield. That creature gains haste until end of turn. Exile it at the beginning of the next end step"; } - + public ShallowGraveEffect(final ShallowGraveEffect effect) { super(effect); } - + @Override public ShallowGraveEffect copy() { return new ShallowGraveEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card lastCreatureCard = null; - for (Card card :controller.getGraveyard().getCards(game)) { + for (Card card : controller.getGraveyard().getCards(game)) { if (card.getCardType().contains(CardType.CREATURE)) { lastCreatureCard = card; - } + } } if (lastCreatureCard != null) { if (controller.putOntoBattlefieldWithInfo(lastCreatureCard, game, Zone.GRAVEYARD, source.getSourceId())) { - FixedTarget fixedTarget = new FixedTarget(lastCreatureCard.getId()); - // Gains Haste - ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - hasteEffect.setTargetPointer(fixedTarget); - game.addEffect(hasteEffect, source); - // Exile it at end of turn - ExileTargetEffect exileEffect = new ExileTargetEffect(null,"",Zone.BATTLEFIELD); - exileEffect.setTargetPointer(fixedTarget); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + Permanent returnedCreature = game.getPermanent(lastCreatureCard.getId()); + if (returnedCreature != null) { + FixedTarget fixedTarget = new FixedTarget(returnedCreature, game); + // Gains Haste + ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + hasteEffect.setTargetPointer(fixedTarget); + game.addEffect(hasteEffect, source); + // Exile it at end of turn + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(fixedTarget); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } } } return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java index f7768675288..e8fcc2a4487 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java @@ -69,8 +69,8 @@ public class PersistTest extends CardTestPlayerBase { // Persist addCard(Zone.BATTLEFIELD, playerA, "Safehold Elite"); - // Exile target card from a graveyard. You gain 3 life. addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + // Exile target card from a graveyard. You gain 3 life. addCard(Zone.HAND, playerB, "Shadowfeed", 1); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); diff --git a/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java index 4f03c6d7a6e..0cd80c15af0 100644 --- a/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java @@ -1,39 +1,40 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.abilities.effects.common; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -85,23 +86,25 @@ public class ExileTargetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + Set toExile = new LinkedHashSet<>(); for (UUID targetId : getTargetPointer().getTargets(game, source)) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { Zone currentZone = game.getState().getZone(permanent.getId()); if (!currentZone.equals(Zone.EXILED) && (onlyFromZone == null || onlyFromZone.equals(Zone.BATTLEFIELD))) { - controller.moveCardToExileWithInfo(permanent, exileId, exileZone, source.getSourceId(), game, currentZone, true); + toExile.add(permanent); } } else { Card card = game.getCard(targetId); if (card != null) { Zone currentZone = game.getState().getZone(card.getId()); if (!currentZone.equals(Zone.EXILED) && (onlyFromZone == null || onlyFromZone.equals(currentZone))) { - controller.moveCardToExileWithInfo(card, exileId, exileZone, source.getSourceId(), game, currentZone, true); + toExile.add(card); } } } } + controller.moveCardsToExile(toExile, source, game, true, exileId, exileZone); return true; } return false; From 3d8494edb5c0fddcb972758f7d983b8b66a9651e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 13 Sep 2015 09:48:35 +0200 Subject: [PATCH 38/39] Fixed some possible exceptions. --- Mage.Common/src/mage/view/GameView.java | 2 +- .../mage/sets/avacynrestored/StolenGoods.java | 24 +++++---- .../sets/exodus/SurvivalOfTheFittest.java | 10 ++-- .../sets/riseoftheeldrazi/SarkhanTheMad.java | 51 +++++++++---------- Mage/src/mage/MageObjectReference.java | 37 ++++++++++---- .../mage/cards/repository/CardRepository.java | 2 +- .../mage/watchers/common/LandfallWatcher.java | 4 +- 7 files changed, 72 insertions(+), 58 deletions(-) diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 3bc9f70e81c..80ce4867c59 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -144,7 +144,7 @@ public class GameView implements Serializable { stack.put(stackObject.getId(), new CardView(((StackAbility) stackObject))); checkPaid(stackObject.getId(), ((StackAbility) stackObject)); } else { - logger.fatal("Object can't be cast to StackAbility: " + object.getName() + " " + object.toString()); + logger.fatal("Object can't be cast to StackAbility: " + object.getName() + " " + object.toString() + " " + object.getClass().toString()); } } } else { diff --git a/Mage.Sets/src/mage/sets/avacynrestored/StolenGoods.java b/Mage.Sets/src/mage/sets/avacynrestored/StolenGoods.java index 7709f8614fb..f6550ca84e3 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/StolenGoods.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/StolenGoods.java @@ -27,21 +27,24 @@ */ package mage.sets.avacynrestored; -import mage.constants.*; +import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.CardsImpl; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; import mage.players.Library; import mage.players.Player; import mage.target.common.TargetOpponent; - -import java.util.UUID; -import mage.MageObject; -import mage.abilities.effects.ContinuousEffect; import mage.target.targetpointer.FixedTarget; /** @@ -54,7 +57,6 @@ public class StolenGoods extends CardImpl { super(ownerId, 78, "Stolen Goods", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{U}"); this.expansionSetCode = "AVR"; - // Target opponent exiles cards from the top of his or her library until he or she exiles a nonland card. Until end of turn, you may cast that card without paying its mana cost. this.getSpellAbility().addEffect(new StolenGoodsEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); @@ -96,14 +98,13 @@ class StolenGoodsEffect extends OneShotEffect { do { card = library.removeFromTop(game); if (card != null) { - opponent.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); + opponent.moveCardsToExile(card, source, game, true, source.getSourceId(), sourceObject.getIdName()); } } while (library.size() > 0 && card != null && card.getCardType().contains(CardType.LAND)); if (card != null) { - opponent.revealCards("Card to cast", new CardsImpl(card), game); ContinuousEffect effect = new StolenGoodsCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); game.addEffect(effect, source); } return true; @@ -135,7 +136,8 @@ class StolenGoodsCastFromExileEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (getTargetPointer().getFirst(game, source).equals(sourceId) && affectedControllerId.equals(source.getControllerId())) { + if (sourceId != null && sourceId.equals(getTargetPointer().getFirst(game, source)) + && affectedControllerId.equals(source.getControllerId())) { Card card = game.getCard(sourceId); if (card != null && game.getState().getZone(sourceId) == Zone.EXILED) { Player player = game.getPlayer(affectedControllerId); diff --git a/Mage.Sets/src/mage/sets/exodus/SurvivalOfTheFittest.java b/Mage.Sets/src/mage/sets/exodus/SurvivalOfTheFittest.java index 02f9e2d5055..36a1fe03ce0 100644 --- a/Mage.Sets/src/mage/sets/exodus/SurvivalOfTheFittest.java +++ b/Mage.Sets/src/mage/sets/exodus/SurvivalOfTheFittest.java @@ -28,16 +28,15 @@ package mage.sets.exodus; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.CardTypePredicate; @@ -49,9 +48,9 @@ import mage.target.common.TargetCardInLibrary; * @author jeffwadsworth */ public class SurvivalOfTheFittest extends CardImpl { - + private static final FilterCard filter = new FilterCard("creature card"); - + static { filter.add(new CardTypePredicate(CardType.CREATURE)); } @@ -60,7 +59,6 @@ public class SurvivalOfTheFittest extends CardImpl { super(ownerId, 129, "Survival of the Fittest", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.expansionSetCode = "EXO"; - // {G}, Discard a creature card: Search your library for a creature card, reveal that card, and put it into your hand. Then shuffle your library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), new ColoredManaCost(ColoredManaSymbol.G)); ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter))); diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SarkhanTheMad.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SarkhanTheMad.java index 9124f195994..8cdd3585f79 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/SarkhanTheMad.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/SarkhanTheMad.java @@ -29,11 +29,8 @@ package mage.sets.riseoftheeldrazi; import java.util.List; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldAbility; @@ -41,8 +38,11 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; @@ -67,7 +67,6 @@ public class SarkhanTheMad extends CardImpl { this.subtype.add("Sarkhan"); this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(7)), false)); - this.addAbility(new LoyaltyAbility(new SarkhanTheMadRevealAndDrawEffect(), 0)); Target targetCreature = new TargetCreaturePermanent(); @@ -94,29 +93,28 @@ class SarkhanTheMadRevealAndDrawEffect extends OneShotEffect { private static final String effectText = "Reveal the top card of your library and put it into your hand. {this} deals damage to himself equal to that card's converted mana cost"; - SarkhanTheMadRevealAndDrawEffect ( ) { + SarkhanTheMadRevealAndDrawEffect() { super(Outcome.DrawCard); staticText = effectText; } - SarkhanTheMadRevealAndDrawEffect ( SarkhanTheMadRevealAndDrawEffect effect ) { + SarkhanTheMadRevealAndDrawEffect(SarkhanTheMadRevealAndDrawEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.getLibrary().size() > 0) { - Card card = player.getLibrary().removeFromTop(game); - Permanent permanent = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + Card card = controller.getLibrary().getFromTop(game); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (card != null) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); - permanent.damage(card.getManaCost().convertedManaCost(), this.getId(), game, false, false); - Cards cards = new CardsImpl(); - cards.add(card); - player.revealCards("Sarkhan the Mad", cards, game); - return true; + controller.moveCards(card, null, Zone.HAND, source, game); + sourcePermanent.damage(card.getManaCost().convertedManaCost(), source.getSourceId(), game, false, false); + controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); } + return true; } return false; } @@ -132,19 +130,19 @@ class SarkhanTheMadSacEffect extends OneShotEffect { private static final String effectText = "Target creature's controller sacrifices it, then that player puts a 5/5 red Dragon creature token with flying onto the battlefield"; - SarkhanTheMadSacEffect ( ) { + SarkhanTheMadSacEffect() { super(Outcome.Sacrifice); staticText = effectText; } - SarkhanTheMadSacEffect ( SarkhanTheMadSacEffect effect ) { + SarkhanTheMadSacEffect(SarkhanTheMadSacEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); - if ( permanent != null ) { + if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); permanent.sacrifice(this.getId(), game); Token dragonToken = new DragonToken(); @@ -171,12 +169,12 @@ class SarkhanTheMadDragonDamageEffect extends OneShotEffect { filter.add(new SubtypePredicate("Dragon")); } - SarkhanTheMadDragonDamageEffect ( ) { + SarkhanTheMadDragonDamageEffect() { super(Outcome.Damage); staticText = effectText; } - SarkhanTheMadDragonDamageEffect ( SarkhanTheMadDragonDamageEffect effect ) { + SarkhanTheMadDragonDamageEffect(SarkhanTheMadDragonDamageEffect effect) { super(effect); } @@ -184,8 +182,8 @@ class SarkhanTheMadDragonDamageEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List dragons = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); Player player = game.getPlayer(source.getTargets().getFirstTarget()); - if ( player != null && dragons != null && !dragons.isEmpty() ) { - for ( Permanent dragon : dragons ) { + if (player != null && dragons != null && !dragons.isEmpty()) { + for (Permanent dragon : dragons) { player.damage(dragon.getPower().getValue(), dragon.getId(), game, true, false); } return true; @@ -202,7 +200,8 @@ class SarkhanTheMadDragonDamageEffect extends OneShotEffect { } class DragonToken extends mage.game.permanent.token.DragonToken { - DragonToken ( ) { + + DragonToken() { super(); this.power = new MageInt(5); this.toughness = new MageInt(5); diff --git a/Mage/src/mage/MageObjectReference.java b/Mage/src/mage/MageObjectReference.java index 8242aff19ad..85edcc3c625 100644 --- a/Mage/src/mage/MageObjectReference.java +++ b/Mage/src/mage/MageObjectReference.java @@ -25,24 +25,27 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage; import java.io.Serializable; import java.util.UUID; import mage.cards.Card; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import org.apache.log4j.Logger; /** * A object reference that takes zone changes into account. * * @author LevelX2 */ - public class MageObjectReference implements Comparable, Serializable { + private static final transient Logger logger = Logger.getLogger(MageObjectReference.class); + private final UUID sourceId; private final int zoneChangeCounter; @@ -50,20 +53,21 @@ public class MageObjectReference implements Comparable, Ser this.sourceId = mageObject.getId(); this.zoneChangeCounter = mageObject.getZoneChangeCounter(game); } + /** - * That values manually (can be used to let it reference to a Permanent - * that is not yet on the battlefield. - * + * That values manually (can be used to let it reference to a Permanent that + * is not yet on the battlefield. + * * @param sourceId * @param zoneChangeCounter - * @param game + * @param game */ public MageObjectReference(UUID sourceId, int zoneChangeCounter, Game game) { this.sourceId = sourceId; this.zoneChangeCounter = zoneChangeCounter; } - public MageObjectReference(UUID sourceId, Game game) { + public MageObjectReference(UUID sourceId, Game game) { this.sourceId = sourceId; MageObject mageObject = game.getObject(sourceId); if (mageObject != null) { @@ -72,7 +76,18 @@ public class MageObjectReference implements Comparable, Ser if (game.getPlayerList().contains(sourceId)) { this.zoneChangeCounter = 0; } else { - throw new IllegalArgumentException("The provided sourceId is not connected to an object in the game"); + logger.error("The provided sourceId is not connected to an object in the game id:" + sourceId); + for (StackObject stackObject : game.getStack()) { + logger.error("StackObject: " + stackObject.getId() + " sourceId" + stackObject.getSourceId() + " name" + stackObject.getName()); + } + mageObject = game.getLastKnownInformation(sourceId, Zone.STACK); + if (mageObject != null) { + this.zoneChangeCounter = mageObject.getZoneChangeCounter(game); + logger.error("SourceId found in LKI"); + } else { + logger.error("SourceId NOT found in LKI"); + this.zoneChangeCounter = 0; + } } } } @@ -96,8 +111,8 @@ public class MageObjectReference implements Comparable, Ser @Override public boolean equals(Object v) { if (v instanceof MageObjectReference) { - if (((MageObjectReference)v).getSourceId().equals(this.sourceId)) { - return ((MageObjectReference)v).getZoneChangeCounter() == this.zoneChangeCounter; + if (((MageObjectReference) v).getSourceId().equals(this.sourceId)) { + return ((MageObjectReference) v).getZoneChangeCounter() == this.zoneChangeCounter; } } return false; @@ -117,7 +132,7 @@ public class MageObjectReference implements Comparable, Ser public boolean refersTo(MageObject mageObject, Game game) { if (mageObject != null) { if (mageObject instanceof Spell) { - return ((Spell)mageObject).getSourceId().equals(sourceId) && this.zoneChangeCounter == mageObject.getZoneChangeCounter(game); + return ((Spell) mageObject).getSourceId().equals(sourceId) && this.zoneChangeCounter == mageObject.getZoneChangeCounter(game); } return mageObject.getId().equals(sourceId) && this.zoneChangeCounter == mageObject.getZoneChangeCounter(game); } diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index f7a2478a817..6cd148fbe91 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -63,7 +63,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 41; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 35; + private static final long CARD_CONTENT_VERSION = 36; private final Random random = new Random(); private Dao cardDao; diff --git a/Mage/src/mage/watchers/common/LandfallWatcher.java b/Mage/src/mage/watchers/common/LandfallWatcher.java index 2b4df7a7a12..d4d14ba0bb1 100644 --- a/Mage/src/mage/watchers/common/LandfallWatcher.java +++ b/Mage/src/mage/watchers/common/LandfallWatcher.java @@ -35,8 +35,8 @@ public class LandfallWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent.getCardType().contains(CardType.LAND) && !playerPlayedLand.contains(event.getPlayerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent != null && permanent.getCardType().contains(CardType.LAND) && !playerPlayedLand.contains(event.getPlayerId())) { playerPlayedLand.add(event.getPlayerId()); } } From ae8ada286cee6a481ef111ff1891e8b4a03c9be1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 13 Sep 2015 10:09:13 +0200 Subject: [PATCH 39/39] xmage_1.4.4v0 --- Mage.Common/src/mage/utils/MageVersion.java | 2 +- Utils/release/getting_implemented_cards.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index fd22657ae44..57351660d59 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 4; - public final static String MAGE_VERSION_MINOR_PATCH = "v0"; + public final static String MAGE_VERSION_MINOR_PATCH = "v3"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index 8afd05edc21..444228c30bb 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -24,6 +24,8 @@ git log 23039572f2206ade860f5835e9b85e82a9c4b2a1..HEAD --diff-filter=A --name-st since 1.4.4.v0 git log 0a458707ddaaa5e7e82ab06d17633084f67077c1..23039572f2206ade860f5835e9b85e82a9c4b2a1 --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.4.v3 +git log 3d8494edb5c0fddcb972758f7d983b8b66a9651e..0a458707ddaaa5e7e82ab06d17633084f67077c1 --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: