From c4dc0222d836fe9d5336b7787b29bb0fd14bcf5b Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 27 Mar 2017 16:42:32 -0500 Subject: [PATCH 1/2] - Fixed Indomitable Creativity. Bug #3029 --- Mage.Sets/src/mage/cards/i/IndomitableCreativity.java | 8 ++++---- .../common/TargetCreaturePermanentSameController.java | 2 +- .../common/TargetCreaturePermanentWithDifferentTypes.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java index 61325514ed..cbbf3d8092 100644 --- a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java +++ b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java @@ -143,12 +143,12 @@ class IndomitableCreativityEffect extends OneShotEffect { cards.add(card); } - if (card.isCreature() || card.isArtifact()) { - controllerOfDestroyedCreature.moveCards(card, Zone.BATTLEFIELD, source, game); - } - if (!cards.isEmpty()) { revealCards.addAll(cards); + if (card.isCreature() || card.isArtifact()) { + controllerOfDestroyedCreature.moveCards(card, Zone.EXILED, source, game); + controllerOfDestroyedCreature.moveCards(card, Zone.BATTLEFIELD, source, game); + } Set cardsToShuffle = cards.getCards(game); cardsToShuffle.remove(card); library.addAll(cardsToShuffle, game); diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java index dc40374086..8a09809ebe 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentSameController.java @@ -56,7 +56,7 @@ public class TargetCreaturePermanentSameController extends TargetCreaturePermane UUID targetId = (UUID) object; Permanent targetPermanent = game.getPermanent(targetId); if (targetPermanent != null) { - if (firstTargetPermanent.getId() != targetPermanent.getId()) { + if (!firstTargetPermanent.getId().equals(targetPermanent.getId())) { if (!firstTargetPermanent.getControllerId().equals(targetPermanent.getOwnerId())) { return false; } diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java index a480187b9c..9d2b1fd48a 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentWithDifferentTypes.java @@ -61,7 +61,7 @@ public class TargetCreaturePermanentWithDifferentTypes extends TargetCreaturePer for (Object object : getTargets()) { UUID targetId = (UUID) object; Permanent selectedCreature = game.getPermanent(targetId); - if (creature.getId() != selectedCreature.getId()) { + if (!creature.getId().equals(selectedCreature.getId())) { if (CardUtil.shareSubtypes(creature, selectedCreature, game)) { return false; } From aa81a6d663d6625c656dee9e5ca7c00ca7512847 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 27 Mar 2017 17:17:56 -0500 Subject: [PATCH 2/2] - LoseAbilityAllEffect() now works with rule 611.2c. --- Mage.Sets/src/mage/cards/g/GravitySphere.java | 17 ++- .../src/mage/cards/i/InvertTheSkies.java | 63 +++------ Mage.Sets/src/mage/cards/m/MysticDecree.java | 6 +- .../mage/cards/t/ThundercloudElemental.java | 14 +- Mage.Sets/src/mage/cards/w/Whiteout.java | 11 +- .../continuous/LoseAbilityAllEffect.java | 122 +++++++++++++++--- 6 files changed, 160 insertions(+), 73 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GravitySphere.java b/Mage.Sets/src/mage/cards/g/GravitySphere.java index 49e8f6c4f0..91b2b9a41c 100644 --- a/Mage.Sets/src/mage/cards/g/GravitySphere.java +++ b/Mage.Sets/src/mage/cards/g/GravitySphere.java @@ -29,6 +29,7 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -36,7 +37,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; /** * @@ -44,12 +46,21 @@ import mage.filter.common.FilterCreaturePermanent; */ public class GravitySphere extends CardImpl { + final static private FilterPermanent filter = new FilterPermanent("All creatures"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + public GravitySphere(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); this.supertype.add("World"); // All creatures lose flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LoseAbilityAllEffect(new FilterCreaturePermanent("All creatures"), FlyingAbility.getInstance(), Duration.WhileOnBattlefield))); + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter); + effect.setText("All creatures lose flying"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } public GravitySphere(final GravitySphere card) { diff --git a/Mage.Sets/src/mage/cards/i/InvertTheSkies.java b/Mage.Sets/src/mage/cards/i/InvertTheSkies.java index 7dd5b30f4b..cc0aa0eaf7 100644 --- a/Mage.Sets/src/mage/cards/i/InvertTheSkies.java +++ b/Mage.Sets/src/mage/cards/i/InvertTheSkies.java @@ -27,51 +27,58 @@ */ package mage.cards.i; -import java.util.Set; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.condition.LockedInCondition; import mage.abilities.condition.common.ManaWasSpentCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; import mage.watchers.common.ManaSpentToCastWatcher; /** * * @author jeffwadsworth - + * */ public class InvertTheSkies extends CardImpl { - public InvertTheSkies(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{G/U}"); + private static final FilterPermanent filter = new FilterPermanent(); + static { + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public InvertTheSkies(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G/U}"); // Creatures your opponents control lose flying until end of turn if {G} was spent to cast Invert the Skies, and creatures you control gain flying until end of turn if {U} was spent to cast it. this.getSpellAbility().addEffect(new ConditionalContinuousEffect( - new InvertTheSkiesEffect(), + new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filter), new LockedInCondition(new ManaWasSpentCondition(ColoredManaSymbol.G)), "Creatures your opponents control lose flying until end of turn if {G} was spent to cast {this},")); + this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new LockedInCondition(new ManaWasSpentCondition(ColoredManaSymbol.U)), "and creatures you control gain flying until end of turn if {U} was spent to cast it")); + this.getSpellAbility().addEffect(new InfoEffect("(Do both if {G}{U} was spent.)")); this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); + } public InvertTheSkies(final InvertTheSkies card) { @@ -83,33 +90,3 @@ public class InvertTheSkies extends CardImpl { return new InvertTheSkies(this); } } - -class InvertTheSkiesEffect extends ContinuousEffectImpl { - - private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - public InvertTheSkiesEffect() { - super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); - } - - public InvertTheSkiesEffect(final InvertTheSkiesEffect effect) { - super(effect); - } - - @Override - public InvertTheSkiesEffect copy() { - return new InvertTheSkiesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Set opponents = game.getOpponents(source.getControllerId()); - for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (opponents.contains(perm.getControllerId())) { - perm.getAbilities().remove(FlyingAbility.getInstance()); - } - } - return true; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MysticDecree.java b/Mage.Sets/src/mage/cards/m/MysticDecree.java index 83dd8b94da..3590beda7d 100644 --- a/Mage.Sets/src/mage/cards/m/MysticDecree.java +++ b/Mage.Sets/src/mage/cards/m/MysticDecree.java @@ -48,13 +48,13 @@ import mage.filter.common.FilterCreaturePermanent; public class MysticDecree extends CardImpl { public MysticDecree(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); this.supertype.add("World"); // All creatures lose flying and islandwalk. - Effect effect = new LoseAbilityAllEffect(new FilterCreaturePermanent("All creatures"), FlyingAbility.getInstance(), Duration.WhileOnBattlefield); + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("All creatures")); effect.setText("All creatures lose flying"); - Effect effect2 = new LoseAbilityAllEffect(new FilterCreaturePermanent("all creatures"), new IslandwalkAbility(), Duration.WhileOnBattlefield); + Effect effect2 = new LoseAbilityAllEffect(new IslandwalkAbility(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("all creatures")); effect2.setText("and islandwalk"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); ability.addEffect(effect2); diff --git a/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java b/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java index 21a034f2a6..b7fca72889 100644 --- a/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java +++ b/Mage.Sets/src/mage/cards/t/ThundercloudElemental.java @@ -31,6 +31,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.TapAllEffect; import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; @@ -49,7 +50,7 @@ import mage.filter.predicate.permanent.AnotherPredicate; * @author fireshoes */ public class ThundercloudElemental extends CardImpl { - + private static final FilterCreaturePermanent toughnessFilter = new FilterCreaturePermanent("creatures with toughness 2 or less"); private static final FilterCreaturePermanent flyingFilter = new FilterCreaturePermanent("All other creatures"); @@ -59,19 +60,22 @@ public class ThundercloudElemental extends CardImpl { } public ThundercloudElemental(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); this.subtype.add("Elemental"); this.power = new MageInt(3); this.toughness = new MageInt(4); // Flying this.addAbility(FlyingAbility.getInstance()); - + // {3}{U}: Tap all creatures with toughness 2 or less. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapAllEffect(toughnessFilter), new ManaCostsImpl("{3}{U}"))); - + // {3}{U}: All other creatures lose flying until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseAbilityAllEffect(flyingFilter, FlyingAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{3}{U}"))); + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, flyingFilter); + effect.setText("All other creatures lose flying until end of turn"); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{U}"))); + } public ThundercloudElemental(final ThundercloudElemental card) { diff --git a/Mage.Sets/src/mage/cards/w/Whiteout.java b/Mage.Sets/src/mage/cards/w/Whiteout.java index fcdd727ca1..4023cba90d 100644 --- a/Mage.Sets/src/mage/cards/w/Whiteout.java +++ b/Mage.Sets/src/mage/cards/w/Whiteout.java @@ -30,6 +30,7 @@ package mage.cards.w; import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; @@ -48,7 +49,7 @@ import mage.target.common.TargetControlledPermanent; * @author fireshoes */ public class Whiteout extends CardImpl { - + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("a snow land"); static { @@ -56,11 +57,13 @@ public class Whiteout extends CardImpl { } public Whiteout(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // All creatures lose flying until end of turn. - this.getSpellAbility().addEffect(new LoseAbilityAllEffect(new FilterCreaturePermanent(), FlyingAbility.getInstance(), Duration.EndOfTurn)); - + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent()); + effect.setText("All creatures lose flying until end of turn"); + this.getSpellAbility().addEffect(effect); + // Sacrifice a snow land: Return Whiteout from your graveyard to your hand. this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnToHandSourceEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java index 4e8e86e908..c7ba03e376 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java @@ -1,40 +1,102 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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.continuous; +import java.util.HashMap; +import java.util.Iterator; +import java.util.UUID; +import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; +import mage.abilities.CompoundAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; /** - * - * @author LevelX2 + * @author BetaSteward_at_googlemail.com */ public class LoseAbilityAllEffect extends ContinuousEffectImpl { - protected final FilterPermanent filter; - protected final Ability ability; + protected CompoundAbility ability; + protected boolean excludeSource; + protected FilterPermanent filter; - public LoseAbilityAllEffect(FilterPermanent filter, Ability ability, Duration duration) { + public LoseAbilityAllEffect(Ability ability, Duration duration) { + this(ability, duration, new FilterPermanent("permanents")); + } + + public LoseAbilityAllEffect(CompoundAbility ability, Duration duration) { + this(ability, duration, new FilterPermanent("permanents")); + } + + public LoseAbilityAllEffect(Ability ability, Duration duration, FilterPermanent filter) { + this(ability, duration, filter, false); + } + + public LoseAbilityAllEffect(CompoundAbility ability, Duration duration, FilterPermanent filter) { + this(ability, duration, filter, false); + } + + public LoseAbilityAllEffect(Ability ability, Duration duration, FilterPermanent filter, boolean excludeSource) { + this(new CompoundAbility(ability), duration, filter, excludeSource); + } + + public LoseAbilityAllEffect(CompoundAbility ability, Duration duration, FilterPermanent filter, boolean excludeSource) { super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.filter = filter; this.ability = ability; - staticText = filter.getMessage() + " lose " + ability.toString() + (duration.toString().isEmpty() ? "" : ' ' + duration.toString()); + this.filter = filter; + this.excludeSource = excludeSource; } public LoseAbilityAllEffect(final LoseAbilityAllEffect effect) { super(effect); + this.ability = effect.ability.copy(); this.filter = effect.filter.copy(); - this.ability = effect.ability; + this.excludeSource = effect.excludeSource; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (this.affectedObjectsSet) { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { + affectedObjectList.add(new MageObjectReference(perm, game)); + } + } + } } @Override @@ -44,10 +106,40 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent != null) { - while (permanent.getAbilities().contains(ability)) { - permanent.getAbilities().remove(ability); + if (this.affectedObjectsSet) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost + Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper) + if (perm != null) { + for (Ability ability : ability) { + perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); + } + } else { + it.remove(); + if (affectedObjectList.isEmpty()) { + discard(); + } + } + } + } else { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { + for (Ability ability : ability) { + perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); + } + } + } + // still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger + HashMap LKIBattlefield = game.getLKI().get(Zone.BATTLEFIELD); + if (LKIBattlefield != null) { + for (MageObject mageObject : LKIBattlefield.values()) { + Permanent perm = (Permanent) mageObject; + if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { + if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) { + for (Ability ability : ability) { + perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId())); + } + } + } } } }