From 956724b5fa48e1bb3c83fb5be417eb1dd272e8da Mon Sep 17 00:00:00 2001 From: Plopman <> Date: Fri, 5 Aug 2016 13:43:23 +0200 Subject: [PATCH 01/24] [TMP] Added 5 cards --- .../src/mage/sets/tempest/ColdStorage.java | 111 ++++++++++++ .../src/mage/sets/tempest/ElvenWarhounds.java | 66 ++++++++ .../src/mage/sets/tempest/Excavator.java | 158 ++++++++++++++++++ .../mage/sets/tempest/PhyrexianGrimoire.java | 141 ++++++++++++++++ Mage.Sets/src/mage/sets/tempest/Watchdog.java | 114 +++++++++++++ 5 files changed, 590 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/tempest/ColdStorage.java create mode 100644 Mage.Sets/src/mage/sets/tempest/ElvenWarhounds.java create mode 100644 Mage.Sets/src/mage/sets/tempest/Excavator.java create mode 100644 Mage.Sets/src/mage/sets/tempest/PhyrexianGrimoire.java create mode 100644 Mage.Sets/src/mage/sets/tempest/Watchdog.java diff --git a/Mage.Sets/src/mage/sets/tempest/ColdStorage.java b/Mage.Sets/src/mage/sets/tempest/ColdStorage.java new file mode 100644 index 0000000000..dc9a34e29d --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/ColdStorage.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.tempest; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author Plopman + */ +public class ColdStorage extends CardImpl { + + public ColdStorage(UUID ownerId) { + super(ownerId, 270, "Cold Storage", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); + this.expansionSetCode = "TMP"; + + // {3}: Exile target creature you control. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), this.getIdName()), new ManaCostsImpl("{3}")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + // Sacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control. + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromExileEffect(this.getId()), new SacrificeSourceCost()); + this.addAbility(ability); + } + + public ColdStorage(final ColdStorage card) { + super(card); + } + + @Override + public ColdStorage copy() { + return new ColdStorage(this); + } +} + + +class ReturnFromExileEffect extends OneShotEffect { + + private UUID exileId; + + public ReturnFromExileEffect(UUID exileId) { + super(Outcome.PutCardInPlay); + this.exileId = exileId; + this.setText("Return each creature card exiled with {this} to the battlefield under your control"); + } + + + public ReturnFromExileEffect(final ReturnFromExileEffect effect) { + super(effect); + this.exileId = effect.exileId; + } + + @Override + public ReturnFromExileEffect copy() { + return new ReturnFromExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ExileZone exile = game.getExile().getExileZone(exileId); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && exile != null) { + controller.moveCards(exile.getCards(new FilterCreatureCard(), game), Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; + } + return false; + } + + +} diff --git a/Mage.Sets/src/mage/sets/tempest/ElvenWarhounds.java b/Mage.Sets/src/mage/sets/tempest/ElvenWarhounds.java new file mode 100644 index 0000000000..4e085d5db4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/ElvenWarhounds.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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Plopman + */ +public class ElvenWarhounds extends CardImpl { + + public ElvenWarhounds(UUID ownerId) { + super(ownerId, 119, "Elven Warhounds", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Hound"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Elven Warhounds becomes blocked by a creature, put that creature on top of its owner's library. + Effect effect = new PutOnLibraryTargetEffect(true); + effect.setText("put that creature on top of its owner's library"); + this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(effect, false)); + } + + public ElvenWarhounds(final ElvenWarhounds card) { + super(card); + } + + @Override + public ElvenWarhounds copy() { + return new ElvenWarhounds(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/Excavator.java b/Mage.Sets/src/mage/sets/tempest/Excavator.java new file mode 100644 index 0000000000..9b9918a9fb --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/Excavator.java @@ -0,0 +1,158 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.tempest; + +import java.util.UUID; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.SourceEffect; +import mage.abilities.keyword.ForestwalkAbility; +import mage.abilities.keyword.IslandwalkAbility; +import mage.abilities.keyword.MountainwalkAbility; +import mage.abilities.keyword.PlainswalkAbility; +import mage.abilities.keyword.SwampwalkAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Plopman + */ +public class Excavator extends CardImpl { + + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("basic land"); + static + { + filter.add(new SupertypePredicate("Basic")); + } + + public Excavator(UUID ownerId) { + super(ownerId, 277, "Excavator", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.expansionSetCode = "TMP"; + + // {tap}, Sacrifice a basic land: Target creature gains landwalk of each of the land types of the sacrificed land until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExcavatorEffect(), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public Excavator(final Excavator card) { + super(card); + } + + @Override + public Excavator copy() { + return new Excavator(this); + } +} + +class ExcavatorEffect extends ContinuousEffectImpl implements SourceEffect { + + private Abilities abilities = new AbilitiesImpl();; + + public ExcavatorEffect() { + super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + setText("Target creature gains landwalk of each of the land types of the sacrificed land until end of turn"); + } + + public ExcavatorEffect(final ExcavatorEffect effect) { + super(effect); + this.abilities = abilities.copy(); + } + + @Override + public ExcavatorEffect copy() { + return new ExcavatorEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + for(Cost cost : source.getCosts()) { + if(cost instanceof SacrificeTargetCost) { + SacrificeTargetCost sacrificeCost = (SacrificeTargetCost) cost; + for(Permanent permanent : sacrificeCost.getPermanents()) { + if(permanent.hasSubtype("Forest")) + { + abilities.add(new ForestwalkAbility()); + } + if(permanent.hasSubtype("Plains")) + { + abilities.add(new PlainswalkAbility()); + } + if(permanent.hasSubtype("Island")) + { + abilities.add(new IslandwalkAbility()); + } + if(permanent.hasSubtype("Mountain")) + { + abilities.add(new MountainwalkAbility()); + } + if(permanent.hasSubtype("Swamp")) + { + abilities.add(new SwampwalkAbility()); + } + } + + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID permanentId : targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanentOrLKIBattlefield(permanentId); + if (permanent != null) { + for(Ability ability : abilities) + { + permanent.addAbility(ability, source.getSourceId(), game, false); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/PhyrexianGrimoire.java b/Mage.Sets/src/mage/sets/tempest/PhyrexianGrimoire.java new file mode 100644 index 0000000000..76baaee712 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/PhyrexianGrimoire.java @@ -0,0 +1,141 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +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.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +/** + * 10/4/2004 : If there is only one card in your graveyard, it is exiled by the first part of the effect and you do not get to put any cards into your hand since the second part fails. + * 10/15/2006: It does not target the cards, but it targets the opponent. If you can't target an opponent, you can't activate this ability. + * + * @author Plopman + */ +public class PhyrexianGrimoire extends CardImpl { + + public PhyrexianGrimoire(UUID ownerId) { + super(ownerId, 291, "Phyrexian Grimoire", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "TMP"; + + // {4}, {tap}: Target opponent chooses one of the top two cards of your graveyard. Exile that card and put the other one into your hand. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhyrexianGrimoireEffect(), new ManaCostsImpl("{4}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + public PhyrexianGrimoire(final PhyrexianGrimoire card) { + super(card); + } + + @Override + public PhyrexianGrimoire copy() { + return new PhyrexianGrimoire(this); + } +} + +class PhyrexianGrimoireEffect extends OneShotEffect { + + public PhyrexianGrimoireEffect() { + super(Outcome.ReturnToHand); + this.staticText = "Target opponent chooses one of the top two cards of your graveyard. Exile that card and put the other one into your hand"; + } + + public PhyrexianGrimoireEffect(final PhyrexianGrimoireEffect effect) { + super(effect); + } + + @Override + public PhyrexianGrimoireEffect copy() { + return new PhyrexianGrimoireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller != null && opponent != null) { + if(controller.getGraveyard().size() > 0) + { + Cards cards = new CardsImpl(); + + UUID card1 = null; + UUID card2 = null; + for (UUID cardId : controller.getGraveyard()) { + card2 = card1; + card1 = cardId; + } + if(card1 != null){ + cards.add(card1); + } + if(card2 != null){ + cards.add(card2); + } + + TargetCard target = new TargetCard(Zone.GRAVEYARD, new FilterCard()); + target.setRequired(true); + if(opponent.choose(Outcome.Exile, cards, target, game)) + { + Card card = game.getCard(target.getFirstTarget()); + cards.remove(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.EXILED, source, game); + } + + if(!cards.isEmpty()) + { + card = game.getCard(cards.iterator().next()); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + } + + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/Watchdog.java b/Mage.Sets/src/mage/sets/tempest/Watchdog.java new file mode 100644 index 0000000000..e322bd5b17 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/Watchdog.java @@ -0,0 +1,114 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.SourceTappedCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.combat.BlocksIfAbleSourceEffect; +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.constants.Zone; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; + +/** + * + * @author Plopman + */ +public class Watchdog extends CardImpl { + + public Watchdog(UUID ownerId) { + super(ownerId, 304, "Watchdog", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Hound"); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Watchdog blocks each turn if able. + this.getAbilities().add(new SimpleStaticAbility(Zone.BATTLEFIELD, new BlocksIfAbleSourceEffect(Duration.WhileOnBattlefield))); + // As long as Watchdog is untapped, all creatures attacking you get -1/-0. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, new WatchdogFilter(), false), new InvertCondition(new SourceTappedCondition()),"As long as {this} is untapped, all creatures attacking you get -1/-0"))); + } + + public Watchdog(final Watchdog card) { + super(card); + } + + @Override + public Watchdog copy() { + return new Watchdog(this); + } +} + + +class WatchdogFilter extends FilterAttackingCreature { + + public WatchdogFilter() { + super("creatures attacking you"); + } + + + public WatchdogFilter(final WatchdogFilter filter) { + super(filter); + } + + @Override + public WatchdogFilter copy() { + return new WatchdogFilter(this); + } + + @Override + public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { + if(!super.match(permanent, sourceId, playerId, game)) { + return false; + } + + for(CombatGroup group : game.getCombat().getGroups()) { + for(UUID attacker : group.getAttackers()) { + if(attacker.equals(permanent.getId())) { + UUID defenderId = group.getDefenderId(); + if(defenderId.equals(playerId)) { + return true; + } + } + } + } + + return false; + } +} From fe2434fa9fe1105c038c55acc3a78e74c0ab2b90 Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Fri, 5 Aug 2016 10:26:47 -0500 Subject: [PATCH 02/24] Implemented Shrink (Homelands) --- .../src/mage/sets/fifthedition/Shrink.java | 52 ++++++++++++++++ Mage.Sets/src/mage/sets/homelands/Shrink.java | 62 +++++++++++++++++++ .../mage/sets/masterseditionii/Shrink.java | 52 ++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/fifthedition/Shrink.java create mode 100644 Mage.Sets/src/mage/sets/homelands/Shrink.java create mode 100644 Mage.Sets/src/mage/sets/masterseditionii/Shrink.java diff --git a/Mage.Sets/src/mage/sets/fifthedition/Shrink.java b/Mage.Sets/src/mage/sets/fifthedition/Shrink.java new file mode 100644 index 0000000000..511057581a --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/Shrink.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.fifthedition; + +import java.util.UUID; + +/** + * + * @author choiseul11 + */ +public class Shrink extends mage.sets.homelands.Shrink { + + public Shrink(UUID ownerId) { + super(ownerId); + this.cardNumber = "188"; + this.expansionSetCode = "5ED"; + } + + public Shrink(final Shrink card) { + super(card); + } + + @Override + public Shrink copy() { + return new Shrink(this); + } +} diff --git a/Mage.Sets/src/mage/sets/homelands/Shrink.java b/Mage.Sets/src/mage/sets/homelands/Shrink.java new file mode 100644 index 0000000000..3ec872acb5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/homelands/Shrink.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.homelands; + +import java.util.UUID; + +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.target.common.TargetCreaturePermanent; + +/** + * + * @author choiseul11 + */ +public class Shrink extends CardImpl { + + public Shrink(UUID ownerId) { + super(ownerId, 71, "Shrink", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); + this.expansionSetCode = "HML"; + + // Target creature gets -5/-0 until end of turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new BoostTargetEffect(-5, 0, Duration.EndOfTurn)); + } + + public Shrink(final Shrink card) { + super(card); + } + + @Override + public Shrink copy() { + return new Shrink(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditionii/Shrink.java b/Mage.Sets/src/mage/sets/masterseditionii/Shrink.java new file mode 100644 index 0000000000..1aed747885 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditionii/Shrink.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.masterseditionii; + +import java.util.UUID; + +/** + * + * @author choiseul11 + */ +public class Shrink extends mage.sets.homelands.Shrink { + + public Shrink(UUID ownerId) { + super(ownerId); + this.cardNumber = "175"; + this.expansionSetCode = "ME2"; + } + + public Shrink(final Shrink card) { + super(card); + } + + @Override + public Shrink copy() { + return new Shrink(this); + } +} From 9b683ef481e437578c88fc313bef682a65d0b333 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Aug 2016 17:33:52 +0200 Subject: [PATCH 03/24] * Fixed that cost modification with target check (Icefall Regent) did not work for flashbacked spells (fixes #1405). --- .../abilities/keywords/FlashbackTest.java | 49 ++++- .../abilities/keyword/FlashbackAbility.java | 7 +- .../main/java/mage/players/PlayerImpl.java | 207 ++++++------------ 3 files changed, 123 insertions(+), 140 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java index 2c7936a1ce..7c8c337630 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java @@ -244,15 +244,15 @@ public class FlashbackTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Silent Departure", 1); assertGraveyardCount(playerA, "Runic Repetition", 1); } - + @Test public void testAltarsReap() { - + addCard(Zone.LIBRARY, playerA, "Island", 2); addCard(Zone.GRAVEYARD, playerA, "Altar's Reap", 1); addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4); addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); setChoice(playerA, "Altar's Reap"); @@ -261,7 +261,48 @@ public class FlashbackTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - + assertGraveyardCount(playerA, "Snapcaster Mage", 1); } + + /** + * Fracturing Gust is bugged. In a match against Affinity, it worked + * properly when cast from hand. When I cast it from graveyard c/o + * Snapcaster Mage flashback, it destroyed my opponent's Darksteel Citadels, + * which it did not do when cast from my hand. + */ + @Test + public void testSnapcasterMageWithIcefallRegent() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + // Destroy target creature. It can't be regenerated. + addCard(Zone.GRAVEYARD, playerA, "Terminate"); + + addCard(Zone.BATTLEFIELD, playerA, "Berserkers' Onslaught", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Icefall Regent", 1); + + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + setChoice(playerA, "Terminate"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback"); // Flashback Terminate + addTarget(playerA, "Icefall Regent"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Snapcaster Mage", 1); + assertGraveyardCount(playerB, "Icefall Regent", 1); + assertExileCount("Terminate", 1); + + assertTappedCount("Mountain", true, 2); + assertTappedCount("Island", true, 2); + assertTappedCount("Swamp", true, 2); + + } + } diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index e1eb17b472..f5726d50f7 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -75,6 +75,7 @@ public class FlashbackAbility extends SpellAbility { this.timing = timingRule; this.usesStack = false; this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + setCostModificationActive(false); } public FlashbackAbility(final FlashbackAbility ability) { @@ -195,7 +196,9 @@ class FlashbackEffect extends OneShotEffect { spellAbility.clear(); // set the payed flashback costs to the spell ability so abilities like Converge or calculation of {X} values work spellAbility.getManaCostsToPay().clear(); - spellAbility.getManaCostsToPay().addAll(source.getManaCostsToPay()); + spellAbility.getManaCostsToPay().addAll(source.getManaCosts()); + spellAbility.getManaCosts().clear(); + spellAbility.getManaCosts().addAll(source.getManaCosts()); // needed to get e.g. paid costs from Conflagrate for (Cost cost : source.getCosts()) { if (!(cost instanceof VariableCost)) { @@ -205,7 +208,7 @@ class FlashbackEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName()); } - spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells + // spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells if (controller.cast(spellAbility, game, false)) { ContinuousEffect effect = new FlashbackReplacementEffect(); effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()))); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 6ef55b92c8..8cb9f10dc8 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -179,7 +179,9 @@ public abstract class PlayerImpl implements Player, Serializable { protected Date dateLastAddedToStack; // F10 protected boolean skippedAtLeastOnce; // used to track if passed started in specific phase /** - * This indicates that player passed all turns until his own turn starts (F9). Note! This differs from passedTurn as it doesn't care about spells and abilities in the stack and will pass them as well. + * This indicates that player passed all turns until his own turn starts + * (F9). Note! This differs from passedTurn as it doesn't care about spells + * and abilities in the stack and will pass them as well. */ protected boolean passedAllTurns; // F9 protected AbilityType justActivatedType; // used to check if priority can be passed automatically @@ -524,15 +526,13 @@ public abstract class PlayerImpl implements Player, Serializable { inRange.add(player.getId()); } } - } - else if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { + } else if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { for (Player player : game.getPlayers().values()) { if (!player.hasLeft()) { inRange.add(player.getId()); } } - } - else { + } else { inRange.add(playerId); PlayerList players = game.getState().getPlayerList(playerId); for (int i = 0; i < range.getRange(); i++) { @@ -595,7 +595,8 @@ public abstract class PlayerImpl implements Player, Serializable { } /** - * returns true if the player has the control itself - false if the player is controlled by another player + * returns true if the player has the control itself - false if the player + * is controlled by another player * * @return */ @@ -679,8 +680,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (card.getOwnerId().equals(playerId)) { card.setZone(Zone.HAND, game); this.hand.add(card); - } - else { + } else { return game.getPlayer(card.getOwnerId()).putInHand(card, game); } return true; @@ -741,8 +741,7 @@ public abstract class PlayerImpl implements Player, Serializable { discard(card, source, game); } } - } - else { + } else { int possibleAmount = Math.min(getHand().size(), amount); TargetDiscard target = new TargetDiscard(possibleAmount, possibleAmount, new FilterCard(CardUtil.numberToText(possibleAmount, "a") + " card" + (possibleAmount > 1 ? "s" : "")), playerId); choose(Outcome.Discard, target, source == null ? null : source.getSourceId(), game); @@ -836,8 +835,7 @@ public abstract class PlayerImpl implements Player, Serializable { Permanent attachedTo = game.getPermanent(permanent.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(permanent.getId(), game); - } - else { + } else { Player attachedToPlayer = game.getPlayer(permanent.getAttachedTo()); if (attachedToPlayer != null) { attachedToPlayer.removeAttachment(permanent, game); @@ -858,8 +856,7 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) { if (card.getOwnerId().equals(playerId)) { this.graveyard.add(card); - } - else { + } else { return game.getPlayer(card.getOwnerId()).putInGraveyard(card, game, fromBattlefield); } return true; @@ -878,8 +875,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (UUID objectId : cards) { moveObjectToLibrary(objectId, source == null ? null : source.getSourceId(), game, false, false); } - } - else { + } else { TargetCard target = new TargetCard(Zone.ALL, new FilterCard("card to put on the bottom of your library (last one chosen will be bottommost)")); target.setRequired(true); while (isInGame() && cards.size() > 1) { @@ -915,8 +911,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (UUID cardId : cards) { moveObjectToLibrary(cardId, sourceId, game, true, false); } - } - else { + } else { TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put on the top of your library (last one chosen will be topmost)")); target.setRequired(true); while (isInGame() && cards.size() > 1) { @@ -940,8 +935,7 @@ public abstract class PlayerImpl implements Player, Serializable { Zone fromZone = game.getState().getZone(objectId); if ((mageObject instanceof Permanent)) { return this.moveCardToLibraryWithInfo((Permanent) mageObject, sourceId, game, fromZone, toTop, withName); - } - else if (mageObject instanceof Card) { + } else if (mageObject instanceof Card) { return this.moveCardToLibraryWithInfo((Card) mageObject, sourceId, game, fromZone, toTop, withName); } } @@ -983,8 +977,7 @@ public abstract class PlayerImpl implements Player, Serializable { boolean result; if (card.getCardType().contains(CardType.LAND)) { result = playLand(card, game, ignoreTiming); - } - else { + } else { result = cast(card.getSpellAbility(), game, noMana); } if (result == false) { @@ -1016,8 +1009,7 @@ public abstract class PlayerImpl implements Player, Serializable { Costs costs = getCastSourceIdCosts(); if (alternateCosts == null) { noMana = true; - } - else { + } else { spellAbility.getManaCosts().clear(); spellAbility.getManaCostsToPay().clear(); spellAbility.getManaCosts().add(alternateCosts.copy()); @@ -1111,8 +1103,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (storedBookmark == -1 || storedBookmark > bookmark) { // e.g. usefull for undo Nykthos, Shrine to Nyx setStoredBookmark(bookmark); } - } - else { + } else { resetStoredBookmark(game); } return true; @@ -1141,10 +1132,9 @@ public abstract class PlayerImpl implements Player, Serializable { } restoreState(bookmark, ability.getRule(), game); } - } - else { + } else { int bookmark = game.bookmarkState(); - if (ability.activate(game, false)) { + if (ability.activate(game, ability instanceof FlashbackAbility)) { ability.resolve(game); game.removeBookmark(bookmark); resetStoredBookmark(game); @@ -1192,8 +1182,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (ability instanceof PlayLandAbility) { Card card = game.getCard(ability.getSourceId()); result = playLand(card, game, false); - } - else { + } else { if (!ability.canActivate(this.playerId, game)) { return false; } @@ -1208,8 +1197,7 @@ public abstract class PlayerImpl implements Player, Serializable { case SPELL: if (ability instanceof FlashbackAbility) { result = playAbility(ability.copy(), game); - } - else { + } else { result = cast((SpellAbility) ability, game, false); } break; @@ -1255,8 +1243,7 @@ public abstract class PlayerImpl implements Player, Serializable { } if (!ability.isUsesStack()) { ability.resolve(game); - } - else { + } else { game.fireEvent(new GameEvent(EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); } game.removeBookmark(bookmark); @@ -1277,8 +1264,7 @@ public abstract class PlayerImpl implements Player, Serializable { useable.clear(); useable.put(ability.getId(), (SpellAbility) ability); return useable; - } - else { + } else { // Fuse only allowed from hand continue; } @@ -1307,12 +1293,10 @@ public abstract class PlayerImpl implements Player, Serializable { if (((ActivatedAbility) ability).canActivate(playerId, game)) { useable.put(ability.getId(), (ActivatedAbility) ability); } - } - else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) { + } else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) { useable.put(ability.getId(), (ActivatedAbility) ability); } - } - else if (ability instanceof AlternativeSourceCosts) { + } else if (ability instanceof AlternativeSourceCosts) { if (object.getCardType().contains(CardType.LAND)) { for (Ability ability2 : object.getAbilities().copy()) { if (ability2 instanceof PlayLandAbility) { @@ -1370,8 +1354,7 @@ public abstract class PlayerImpl implements Player, Serializable { // Left Half if (card.getCardType().contains(CardType.INSTANT)) { flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.INSTANT); - } - else { + } else { flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.SORCERY); } flashbackAbility.setSourceId(card.getId()); @@ -1384,8 +1367,7 @@ public abstract class PlayerImpl implements Player, Serializable { // Right Half if (card.getCardType().contains(CardType.INSTANT)) { flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.INSTANT); - } - else { + } else { flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.SORCERY); } flashbackAbility.setSourceId(card.getId()); @@ -1396,8 +1378,7 @@ public abstract class PlayerImpl implements Player, Serializable { useable.put(flashbackAbility.getId(), flashbackAbility); } - } - else { + } else { useable.put(ability.getId(), ability); } } @@ -1453,8 +1434,7 @@ public abstract class PlayerImpl implements Player, Serializable { public void revealCards(String name, Cards cards, Game game, boolean postToLog) { if (postToLog) { game.getState().getRevealed().add(name, cards); - } - else { + } else { game.getState().getRevealed().update(name, cards); } if (postToLog && !game.isSimulation()) { @@ -1586,8 +1566,7 @@ public abstract class PlayerImpl implements Player, Serializable { } } - } - else { + } else { // player selected an permanent that is restricted by another effect, disallow it (so AI can select another one) filter.add(Predicates.not(new PermanentIdPredicate(selectedPermanent.getId()))); if (this.isHuman() && !game.isSimulation()) { @@ -1625,8 +1604,7 @@ public abstract class PlayerImpl implements Player, Serializable { } - } - else { + } else { //20091005 - 502.2 for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { boolean untap = true; @@ -1721,8 +1699,7 @@ public abstract class PlayerImpl implements Player, Serializable { // rule 118.5 if (life > this.life) { gainLife(life - this.life, game); - } - else if (life < this.life) { + } else if (life < this.life) { loseLife(this.life - life, game); } } @@ -1822,31 +1799,26 @@ public abstract class PlayerImpl implements Player, Serializable { StackObject stackObject = game.getStack().getStackObject(sourceId); if (stackObject != null) { source = stackObject.getStackAbility().getSourceObject(game); - } - else { + } else { source = game.getObject(sourceId); } if (source instanceof Spell) { sourceAbilities = ((Spell) source).getAbilities(game); sourceControllerId = ((Spell) source).getControllerId(); - } - else if (source instanceof Card) { + } else if (source instanceof Card) { sourceAbilities = ((Card) source).getAbilities(game); sourceControllerId = ((Card) source).getOwnerId(); - } - else if (source instanceof CommandObject) { + } else if (source instanceof CommandObject) { sourceControllerId = ((CommandObject) source).getControllerId(); sourceAbilities = ((CommandObject) source).getAbilities(); } - } - else { + } else { sourceAbilities = ((Permanent) source).getAbilities(game); sourceControllerId = ((Permanent) source).getControllerId(); } if (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId())) { addCounters(CounterType.POISON.createInstance(actualDamage), game); - } - else { + } else { GameEvent damageToLifeLossEvent = new GameEvent(EventType.DAMAGE_CAUSES_LIFE_LOSS, playerId, sourceId, playerId, actualDamage, combatDamage); if (!game.replaceEvent(damageToLifeLossEvent)) { this.loseLife(damageToLifeLossEvent.getAmount(), game); @@ -2163,8 +2135,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.loses = true; game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LOST, null, null, playerId)); game.informPlayers(this.getLogName() + " has lost the game."); - } - else { + } else { logger.debug(this.getName() + " has already won - stop lost"); } // for draw - first all players that have lost have to be set to lost @@ -2219,8 +2190,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.wins = true; game.end(); } - } - else { + } else { logger.debug("player won -> but already lost before: " + this.getName()); } } @@ -2245,8 +2215,7 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean hasWon() { if (!this.loses) { return this.wins; - } - else { + } else { return false; } } @@ -2274,8 +2243,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (blocker != null && group != null && group.canBlock(blocker, game)) { group.addBlocker(blockerId, playerId, game); game.getCombat().addBlockingGroup(blockerId, attackerId, playerId, game); - } - else if (this.isHuman() && !game.isSimulation()) { + } else if (this.isHuman() && !game.isSimulation()) { game.informPlayer(this, "You can't block this creature."); } } @@ -2293,8 +2261,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (targetPlayerId.equals(playerId)) { searchInfo = getLogName() + " searches his or her library"; searchedLibrary = library; - } - else { + } else { Player targetPlayer = game.getPlayer(targetPlayerId); if (targetPlayer != null) { searchInfo = getLogName() + " searches the library of " + targetPlayer.getLogName(); @@ -2314,8 +2281,7 @@ public abstract class PlayerImpl implements Player, Serializable { int librarySearchLimit = event.getAmount(); if (librarySearchLimit == Integer.MAX_VALUE) { count = searchedLibrary.count(target.getFilter(), game); - } - else { + } else { newTarget.setCardLimit(librarySearchLimit); count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit); } @@ -2408,8 +2374,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (canAdd) { if (withCost) { sourceWithCosts.add(manaAbilities); - } - else { + } else { sourceWithoutManaCosts.add(manaAbilities); } } @@ -2513,8 +2478,7 @@ public abstract class PlayerImpl implements Player, Serializable { ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions(); if (abilityOptions.isEmpty()) { return true; - } - else { + } else { if (available == null) { return true; } @@ -2562,8 +2526,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.size() == 0) { return true; - } - else { + } else { if (available == null) { return true; } @@ -2594,8 +2557,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.size() == 0) { return true; - } - else { + } else { for (Mana mana : manaCosts.getOptions()) { for (Mana avail : available) { if (mana.enough(avail)) { @@ -2632,8 +2594,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.size() == 0) { return true; - } - else { + } else { for (Mana mana : manaCosts.getOptions()) { for (Mana avail : available) { if (mana.enough(avail)) { @@ -2670,14 +2631,12 @@ public abstract class PlayerImpl implements Player, Serializable { playable.add(ability); } } - } - else if (ability instanceof AlternativeSourceCosts) { + } else if (ability instanceof AlternativeSourceCosts) { if (card.getCardType().contains(CardType.LAND)) { if (canLandPlayAlternateSourceCostsAbility(card, availableMana, ability, game)) { // e.g. Land with Morph playable.add(ability); } - } - else if (card.getCardType().contains(CardType.CREATURE)) { // e.g. makes a card available for play by Morph if the card may not be cast normally + } else if (card.getCardType().contains(CardType.CREATURE)) { // e.g. makes a card available for play by Morph if the card may not be cast normally if (!playable.contains(card.getSpellAbility())) { if (((AlternativeSourceCosts) ability).isAvailable(card.getSpellAbility(), game)) { playable.add(card.getSpellAbility()); @@ -2695,8 +2654,7 @@ public abstract class PlayerImpl implements Player, Serializable { boolean possible = false; if (ability.getZone().match(Zone.GRAVEYARD)) { possible = true; - } - else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) { + } else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) { if (asThoughtCast || canPlayCardsFromGraveyard()) { possible = true; } @@ -2847,7 +2805,8 @@ public abstract class PlayerImpl implements Player, Serializable { } /** - * Skip "silent" phase step when players are not allowed to cast anything. E.g. players can't play or cast anything during declaring attackers. + * Skip "silent" phase step when players are not allowed to cast anything. + * E.g. players can't play or cast anything during declaring attackers. * * @param game * @return @@ -2879,17 +2838,14 @@ public abstract class PlayerImpl implements Player, Serializable { if (ability.isModal()) { addModeOptions(options, ability, game); - } - else if (ability.getTargets().getUnchosen().size() > 0) { + } else if (ability.getTargets().getUnchosen().size() > 0) { // TODO: Handle other variable costs than mana costs if (ability.getManaCosts().getVariableCosts().size() > 0) { addVariableXOptions(options, ability, 0, game); - } - else { + } else { addTargetOptions(options, ability, 0, game); } - } - else if (ability.getCosts().getTargets().getUnchosen().size() > 0) { + } else if (ability.getCosts().getTargets().getUnchosen().size() > 0) { addCostTargetOptions(options, ability, 0, game); } @@ -2906,15 +2862,12 @@ public abstract class PlayerImpl implements Player, Serializable { if (newOption.getTargets().getUnchosen().size() > 0) { if (newOption.getManaCosts().getVariableCosts().size() > 0) { addVariableXOptions(options, newOption, 0, game); - } - else { + } else { addTargetOptions(options, newOption, 0, game); } - } - else if (newOption.getCosts().getTargets().getUnchosen().size() > 0) { + } else if (newOption.getCosts().getTargets().getUnchosen().size() > 0) { addCostTargetOptions(options, newOption, 0, game); - } - else { + } else { options.add(newOption); } } @@ -2932,19 +2885,16 @@ public abstract class PlayerImpl implements Player, Serializable { int amount = target.getTargetAmount(targetId); newOption.getTargets().get(targetNum).addTarget(targetId, amount, newOption, game, true); } - } - else { + } else { for (UUID targetId : target.getTargets()) { newOption.getTargets().get(targetNum).addTarget(targetId, newOption, game, true); } } if (targetNum < option.getTargets().size() - 2) { addTargetOptions(options, newOption, targetNum + 1, game); - } - else if (option.getCosts().getTargets().size() > 0) { + } else if (option.getCosts().getTargets().size() > 0) { addCostTargetOptions(options, newOption, 0, game); - } - else { + } else { options.add(newOption); } } @@ -2956,8 +2906,7 @@ public abstract class PlayerImpl implements Player, Serializable { newOption.getCosts().getTargets().get(targetNum).addTarget(targetId, option, game, true); if (targetNum < option.getCosts().getTargets().size() - 1) { addCostTargetOptions(options, newOption, targetNum + 1, game); - } - else { + } else { options.add(newOption); } } @@ -3165,16 +3114,14 @@ public abstract class PlayerImpl implements Player, Serializable { if (permanent != null) { cardList.add(permanent); } - } - else { + } else { Card card = game.getCard(cardId); if (card == null) { Spell spell = game.getState().getStack().getSpell(cardId); if (spell != null) { if (!spell.isCopy()) { card = spell.getCard(); - } - else { + } else { // If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve game.getStack().remove(spell); game.informPlayers(spell.getLogName() + " was removed from the stack"); @@ -3272,8 +3219,7 @@ public abstract class PlayerImpl implements Player, Serializable { Permanent permanent; if (card instanceof MeldCard) { permanent = new PermanentMeld(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now - } - else { + } else { permanent = new PermanentCard(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now } permanents.add(permanent); @@ -3293,8 +3239,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId()); if (permanent.entersBattlefield(source.getSourceId(), game, fromZone, true)) { permanentsEntered.add(permanent); - } - else { + } else { // revert controller to owner if permanent does not enter game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId()); game.getPermanentsEntering().remove(permanent.getId()); @@ -3314,8 +3259,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.informPlayers(this.getLogName() + " puts " + (faceDown ? "a card face down " : permanent.getLogName()) + " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield"); } - } - else { + } else { game.getPermanentsEntering().remove(permanent.getId()); } } @@ -3349,8 +3293,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (Card card : cards) { if (card instanceof Spell) { fromZone = game.getState().getZone(((Spell) card).getSourceId()); - } - else { + } else { fromZone = game.getState().getZone(card.getId()); } boolean hideCard = fromZone.equals(Zone.HAND) || fromZone.equals(Zone.LIBRARY); @@ -3468,8 +3411,7 @@ public abstract class PlayerImpl implements Player, Serializable { movedCards.add(card); } } - } - else { + } else { for (Card card : cards.getCards(game)) { if (choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone)) { movedCards.add(card); @@ -3495,8 +3437,7 @@ public abstract class PlayerImpl implements Player, Serializable { .append(fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + " " : ""); if (card.getOwnerId().equals(getId())) { sb.append("into his or her graveyard"); - } - else { + } else { sb.append("it into its owner's graveyard"); } game.informPlayers(sb.toString()); @@ -3522,8 +3463,7 @@ public abstract class PlayerImpl implements Player, Serializable { sb.append("to the ").append(toTop ? "top" : "bottom"); if (card.getOwnerId().equals(getId())) { sb.append(" of his or her library"); - } - else { + } else { Player player = game.getPlayer(card.getOwnerId()); if (player != null) { sb.append(" of ").append(player.getLogName()).append("'s library"); @@ -3655,8 +3595,7 @@ public abstract class PlayerImpl implements Player, Serializable { String text; if (cards.size() == 1) { text = "card if you want to put it to the bottom of your library (Scry)"; - } - else { + } else { text = "cards you want to put on the bottom of your library (Scry)"; } TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard(text)); From d1c25b0662b7db89281113fd2815a1336ac8cc32 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Aug 2016 20:03:04 +0200 Subject: [PATCH 04/24] * Fixed flashback check of Altar of the Lost conditional mana. --- .../mage/sets/darkascension/AltarOfTheLost.java | 16 +++++++++------- .../test/cards/single/AltarOfTheLostTest.java | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/darkascension/AltarOfTheLost.java b/Mage.Sets/src/mage/sets/darkascension/AltarOfTheLost.java index 8c2e3f7429..f13cb857e1 100644 --- a/Mage.Sets/src/mage/sets/darkascension/AltarOfTheLost.java +++ b/Mage.Sets/src/mage/sets/darkascension/AltarOfTheLost.java @@ -27,10 +27,8 @@ */ package mage.sets.darkascension; +import java.util.UUID; import mage.ConditionalMana; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; @@ -40,9 +38,11 @@ import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ConditionalAnyColorManaAbility; import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; - -import java.util.UUID; +import mage.game.stack.Spell; /** * @@ -72,6 +72,7 @@ public class AltarOfTheLost extends CardImpl { } class AltarOfTheLostManaBuilder extends ConditionalManaBuilder { + @Override public ConditionalMana build(Object... options) { return new AltarOfTheLostConditionalMana(this.mana); @@ -93,11 +94,12 @@ class AltarOfTheLostConditionalMana extends ConditionalMana { } class AltarOfTheLostManaCondition implements Condition { + @Override public boolean apply(Game game, Ability source) { MageObject object = game.getObject(source.getSourceId()); - if (object != null && game.getState().getZone(object.getId()) == Zone.GRAVEYARD) { - for (Ability ability: object.getAbilities()) { + if (object instanceof Spell && ((Spell) object).getFromZone().equals(Zone.GRAVEYARD)) { + for (Ability ability : ((Spell) object).getAbilities(game)) { if (ability instanceof FlashbackAbility) { return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java index 1fbda75a48..b130ef77e5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/AltarOfTheLostTest.java @@ -13,7 +13,11 @@ public class AltarOfTheLostTest extends CardTestPlayerBase { @Test public void testCard() { + // Altar of the Lost enters the battlefield tapped. + // {tap}: Add two mana in any combination of colors to your mana pool. Spend this mana only to cast spells with flashback from a graveyard. addCard(Zone.BATTLEFIELD, playerA, "Altar of the Lost"); + // Put two 1/1 white Spirit creature tokens with flying onto the battlefield. + // Flashback {1}{B} addCard(Zone.GRAVEYARD, playerA, "Lingering Souls"); setChoice(playerA, "Black"); From d0db2d51eddf7bca88293b7da4f5e86e3db27cdf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Aug 2016 21:15:07 +0200 Subject: [PATCH 05/24] * Fixed Identity Thief copying creature with +1/+1 counter gets P/T boost from it, but not counter (fixes #2131). --- .../commander2013/MarathWillOfTheWild.java | 4 +- .../src/mage/sets/darksteel/WireflyHive.java | 4 +- .../mage/sets/eldritchmoon/IdentityThief.java | 11 +- .../sets/eldritchmoon/TreeOfPerdition.java | 7 +- .../fallenempires/HomaridSpawningBed.java | 4 +- .../mage/sets/fifthedition/PrimalClay.java | 6 +- .../src/mage/sets/gatecrash/MimingSlime.java | 234 +++++++++--------- .../src/mage/sets/gatecrash/OozeFlux.java | 218 ++++++++-------- .../src/mage/sets/homelands/DrudgeSpell.java | 4 +- .../mage/sets/innistrad/EssenceOfTheWild.java | 6 +- .../PharikaGodOfAffliction.java | 15 +- .../sets/magicorigins/NissaSageAnimist.java | 4 +- .../mirrodinbesieged/PhyrexianRebirth.java | 4 +- .../sets/planarchaos/JeditOjanenOfEfrava.java | 4 +- .../sets/planechase2012/PrimalPlasma.java | 6 +- .../src/mage/sets/ravnica/MoltenSentry.java | 11 +- .../sets/returntoravnica/SlimeMolding.java | 4 +- .../riseoftheeldrazi/DevastatingSummons.java | 4 +- .../riseoftheeldrazi/GelatinousGenesis.java | 4 +- .../QuicksilverGargantuan.java | 8 +- .../src/mage/sets/timespiral/Wurmcalling.java | 4 +- .../sets/urzassaga/PhyrexianProcessor.java | 4 +- .../cards/abilities/keywords/MorphTest.java | 78 +++--- .../java/org/mage/test/player/TestPlayer.java | 6 +- Mage/src/main/java/mage/MageInt.java | 41 ++- .../BecomesFaceDownCreatureEffect.java | 38 ++- .../common/continuous/BoostTargetEffect.java | 2 +- .../abilities/keyword/FlashbackAbility.java | 1 - .../mage/abilities/keyword/MorphAbility.java | 4 +- .../mage/game/permanent/PermanentCard.java | 12 +- .../mage/game/permanent/PermanentToken.java | 4 +- .../java/mage/game/permanent/token/Token.java | 30 +-- .../util/functions/CopyTokenFunction.java | 6 +- 33 files changed, 407 insertions(+), 385 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander2013/MarathWillOfTheWild.java b/Mage.Sets/src/mage/sets/commander2013/MarathWillOfTheWild.java index f24bb796b4..f56934af52 100644 --- a/Mage.Sets/src/mage/sets/commander2013/MarathWillOfTheWild.java +++ b/Mage.Sets/src/mage/sets/commander2013/MarathWillOfTheWild.java @@ -158,8 +158,8 @@ class MarathWillOfTheWildCreateTokenEffect extends OneShotEffect { if (player != null) { int amount = new ManacostVariableValue().calculate(game, source, this); Token token = new MarathWillOfTheWildElementalToken(); - token.getPower().initValue(amount); - token.getToughness().initValue(amount); + token.getPower().modifyBaseValue(amount); + token.getToughness().modifyBaseValue(amount); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/darksteel/WireflyHive.java b/Mage.Sets/src/mage/sets/darksteel/WireflyHive.java index fc3441c4d5..0bdba0a96c 100644 --- a/Mage.Sets/src/mage/sets/darksteel/WireflyHive.java +++ b/Mage.Sets/src/mage/sets/darksteel/WireflyHive.java @@ -113,8 +113,8 @@ class WireflyToken extends Token { WireflyToken() { super("Wirefly", "2/2 colorless Insect artifact creature token named Wirefly"); this.setOriginalExpansionSetCode("DST"); - this.getPower().initValue(2); - this.getToughness().initValue(2); + this.getPower().modifyBaseValue(2); + this.getToughness().modifyBaseValue(2); this.getSubtype().add("Insect"); this.getCardType().add(CardType.ARTIFACT); this.getCardType().add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/IdentityThief.java b/Mage.Sets/src/mage/sets/eldritchmoon/IdentityThief.java index 9e9400b86c..12cbb8c3b6 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/IdentityThief.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/IdentityThief.java @@ -106,10 +106,7 @@ class IdentityThiefAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - return true; - } - return false; + return event.getSourceId().equals(this.getSourceId()); } @Override @@ -141,7 +138,11 @@ class IdentityThiefEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (controller != null && permanent != null && sourcePermanent != null) { - CopyEffect copyEffect = new CopyEffect(Duration.EndOfTurn, permanent, source.getSourceId()); + Permanent permanentReset = permanent.copy(); + permanentReset.getCounters().clear(); + permanentReset.getPower().resetToBaseValue(); + permanentReset.getToughness().resetToBaseValue(); + CopyEffect copyEffect = new CopyEffect(Duration.EndOfTurn, permanentReset, source.getSourceId()); if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { // Copy exiled permanent game.addEffect(copyEffect, source); diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/TreeOfPerdition.java b/Mage.Sets/src/mage/sets/eldritchmoon/TreeOfPerdition.java index b0fb21ace2..9e0b05f8d8 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/TreeOfPerdition.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/TreeOfPerdition.java @@ -33,14 +33,11 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -108,7 +105,9 @@ class TreeOfPerditionEffect extends OneShotEffect { return false; } opponent.setLife(amount, game); - game.addEffect(new SetPowerToughnessSourceEffect(Integer.MIN_VALUE, life, Duration.Custom, SubLayer.SetPT_7b), source); + perm.getPower().modifyBaseValue(life); + perm.getToughness().modifyBaseValue(life); + // game.addEffect(new SetPowerToughnessSourceEffect(Integer.MIN_VALUE, life, Duration.Custom, SubLayer.SetPT_7b), source); return true; } } diff --git a/Mage.Sets/src/mage/sets/fallenempires/HomaridSpawningBed.java b/Mage.Sets/src/mage/sets/fallenempires/HomaridSpawningBed.java index 9fc07352c2..eac5d491f8 100644 --- a/Mage.Sets/src/mage/sets/fallenempires/HomaridSpawningBed.java +++ b/Mage.Sets/src/mage/sets/fallenempires/HomaridSpawningBed.java @@ -82,8 +82,8 @@ class CamaridToken extends Token { CamaridToken() { super("Camarid", "1/1 blue Camarid creature tokens"); this.setOriginalExpansionSetCode("FEM"); - this.getPower().initValue(1); - this.getToughness().initValue(1); + this.getPower().modifyBaseValue(1); + this.getToughness().modifyBaseValue(1); this.color.setBlue(true); this.getSubtype().add("Camarid"); this.getCardType().add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/fifthedition/PrimalClay.java b/Mage.Sets/src/mage/sets/fifthedition/PrimalClay.java index 6057b91fbc..eb7f8e61a6 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/PrimalClay.java +++ b/Mage.Sets/src/mage/sets/fifthedition/PrimalClay.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -43,7 +42,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; import mage.constants.Zone; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -152,7 +150,9 @@ class PrimalPlasmaReplacementEffect extends ReplacementEffectImpl { game.addEffect(new GainAbilitySourceEffect(DefenderAbility.getInstance(), Duration.Custom), source); break; } - game.addEffect(new SetPowerToughnessSourceEffect(power, toughness, Duration.Custom, SubLayer.SetPT_7b), source); + permanent.getPower().modifyBaseValue(power); + permanent.getToughness().modifyBaseValue(toughness); + // game.addEffect(new SetPowerToughnessSourceEffect(power, toughness, Duration.Custom, SubLayer.SetPT_7b), source); } return false; diff --git a/Mage.Sets/src/mage/sets/gatecrash/MimingSlime.java b/Mage.Sets/src/mage/sets/gatecrash/MimingSlime.java index a6c1216c76..1146f4d423 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/MimingSlime.java +++ b/Mage.Sets/src/mage/sets/gatecrash/MimingSlime.java @@ -1,117 +1,117 @@ -/* - * 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.List; -import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.Token; -import mage.players.Player; - -/** - * - * @author LevelX2 - */ -public class MimingSlime extends CardImpl { - - public MimingSlime(UUID ownerId) { - super(ownerId, 126, "Miming Slime", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{G}"); - this.expansionSetCode = "GTC"; - - // Put an X/X green Ooze creature token onto the battlefield, where X is the greatest power among creatures you control. - this.getSpellAbility().addEffect(new MimingSlimeEffect()); - } - - public MimingSlime(final MimingSlime card) { - super(card); - } - - @Override - public MimingSlime copy() { - return new MimingSlime(this); - } -} - -class MimingSlimeEffect extends OneShotEffect { - - public MimingSlimeEffect() { - super(Outcome.PutCreatureInPlay); - staticText = "Put an X/X green Ooze creature token onto the battlefield, where X is the greatest power among creatures you control"; - } - - public MimingSlimeEffect(final MimingSlimeEffect effect) { - super(effect); - } - - @Override - public MimingSlimeEffect copy() { - return new MimingSlimeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game); - int amount = 0; - for (Permanent creature : creatures) { - int power = creature.getPower().getValue(); - if (amount < power) { - amount = power; - } - } - OozeToken oozeToken = new OozeToken(); - oozeToken.getPower().initValue(amount); - oozeToken.getToughness().initValue(amount); - oozeToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } - return false; - } -} - -class OozeToken extends Token { - public OozeToken() { - super("Ooze", "X/X green Ooze creature token"); - cardType.add(CardType.CREATURE); - subtype.add("Ooze"); - color.setGreen(true); - power = new MageInt(0); - toughness = new MageInt(0); - setOriginalExpansionSetCode("RTR"); - } -} +/* + * 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.List; +import java.util.UUID; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Token; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class MimingSlime extends CardImpl { + + public MimingSlime(UUID ownerId) { + super(ownerId, 126, "Miming Slime", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{G}"); + this.expansionSetCode = "GTC"; + + // Put an X/X green Ooze creature token onto the battlefield, where X is the greatest power among creatures you control. + this.getSpellAbility().addEffect(new MimingSlimeEffect()); + } + + public MimingSlime(final MimingSlime card) { + super(card); + } + + @Override + public MimingSlime copy() { + return new MimingSlime(this); + } +} + +class MimingSlimeEffect extends OneShotEffect { + + public MimingSlimeEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "Put an X/X green Ooze creature token onto the battlefield, where X is the greatest power among creatures you control"; + } + + public MimingSlimeEffect(final MimingSlimeEffect effect) { + super(effect); + } + + @Override + public MimingSlimeEffect copy() { + return new MimingSlimeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + List creatures = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), player.getId(), game); + int amount = 0; + for (Permanent creature : creatures) { + int power = creature.getPower().getValue(); + if (amount < power) { + amount = power; + } + } + OozeToken oozeToken = new OozeToken(); + oozeToken.getPower().modifyBaseValue(amount); + oozeToken.getToughness().modifyBaseValue(amount); + oozeToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + return true; + } + return false; + } +} + +class OozeToken extends Token { + public OozeToken() { + super("Ooze", "X/X green Ooze creature token"); + cardType.add(CardType.CREATURE); + subtype.add("Ooze"); + color.setGreen(true); + power = new MageInt(0); + toughness = new MageInt(0); + setOriginalExpansionSetCode("RTR"); + } +} diff --git a/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java b/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java index 8fdb455bce..074fec116f 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java +++ b/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java @@ -1,109 +1,109 @@ -/* - * 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; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.common.RemoveVariableCountersTargetCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.CardImpl; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.token.Token; - -/** - * - * @author LevelX2 - */ -public class OozeFlux extends CardImpl { - - public OozeFlux(UUID ownerId) { - super(ownerId, 128, "Ooze Flux", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); - this.expansionSetCode = "GTC"; - - // {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OozeFluxCreateTokenEffect(new OozeToken()),new ManaCostsImpl("{1}{G}")); - ability.addCost(new RemoveVariableCountersTargetCost(new FilterControlledCreaturePermanent("creatures you control"), CounterType.P1P1, "one or more", 1)); - this.addAbility(ability); - } - - public OozeFlux(final OozeFlux card) { - super(card); - } - - @Override - public OozeFlux copy() { - return new OozeFlux(this); - } -} - -class OozeFluxCreateTokenEffect extends OneShotEffect { - - private final Token token; - - public OozeFluxCreateTokenEffect(Token token) { - super(Outcome.PutCreatureInPlay); - this.token = token; - staticText = "Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way"; - } - - public OozeFluxCreateTokenEffect(final OozeFluxCreateTokenEffect effect) { - super(effect); - this.token = effect.token.copy(); - } - - @Override - public OozeFluxCreateTokenEffect copy() { - return new OozeFluxCreateTokenEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int xValue = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersTargetCost) { - xValue = ((RemoveVariableCountersTargetCost) cost).getAmount(); - break; - } - } - Token tokenCopy = token.copy(); - tokenCopy.getAbilities().newId(); - tokenCopy.getPower().initValue(xValue); - tokenCopy.getToughness().initValue(xValue); - tokenCopy.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - return true; - } -} +/* + * 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; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveVariableCountersTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.token.Token; + +/** + * + * @author LevelX2 + */ +public class OozeFlux extends CardImpl { + + public OozeFlux(UUID ownerId) { + super(ownerId, 128, "Ooze Flux", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + this.expansionSetCode = "GTC"; + + // {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OozeFluxCreateTokenEffect(new OozeToken()),new ManaCostsImpl("{1}{G}")); + ability.addCost(new RemoveVariableCountersTargetCost(new FilterControlledCreaturePermanent("creatures you control"), CounterType.P1P1, "one or more", 1)); + this.addAbility(ability); + } + + public OozeFlux(final OozeFlux card) { + super(card); + } + + @Override + public OozeFlux copy() { + return new OozeFlux(this); + } +} + +class OozeFluxCreateTokenEffect extends OneShotEffect { + + private final Token token; + + public OozeFluxCreateTokenEffect(Token token) { + super(Outcome.PutCreatureInPlay); + this.token = token; + staticText = "Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way"; + } + + public OozeFluxCreateTokenEffect(final OozeFluxCreateTokenEffect effect) { + super(effect); + this.token = effect.token.copy(); + } + + @Override + public OozeFluxCreateTokenEffect copy() { + return new OozeFluxCreateTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = 0; + for (Cost cost : source.getCosts()) { + if (cost instanceof RemoveVariableCountersTargetCost) { + xValue = ((RemoveVariableCountersTargetCost) cost).getAmount(); + break; + } + } + Token tokenCopy = token.copy(); + tokenCopy.getAbilities().newId(); + tokenCopy.getPower().modifyBaseValue(xValue); + tokenCopy.getToughness().modifyBaseValue(xValue); + tokenCopy.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/homelands/DrudgeSpell.java b/Mage.Sets/src/mage/sets/homelands/DrudgeSpell.java index 12b236a2a1..1600dac4b5 100644 --- a/Mage.Sets/src/mage/sets/homelands/DrudgeSpell.java +++ b/Mage.Sets/src/mage/sets/homelands/DrudgeSpell.java @@ -91,8 +91,8 @@ class SkeletonToken extends Token { SkeletonToken() { super("Skeleton", "1/1 black Skeleton creature token onto the battlefield. It has \"{B}: Regenerate this creature.\""); this.setOriginalExpansionSetCode("HML"); - this.getPower().initValue(1); - this.getToughness().initValue(1); + this.getPower().modifyBaseValue(1); + this.getToughness().modifyBaseValue(1); this.color.setBlack(true); this.getSubtype().add("Skeleton"); this.getCardType().add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java index 982232021c..7372a9e86b 100644 --- a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java +++ b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java @@ -99,7 +99,11 @@ class EssenceOfTheWildEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourceObject != null) { - game.addEffect(new CopyEffect(Duration.Custom, sourceObject, event.getTargetId()), source); + Permanent permanentReset = sourceObject.copy(); + permanentReset.getCounters().clear(); + permanentReset.getPower().resetToBaseValue(); + permanentReset.getToughness().resetToBaseValue(); + game.addEffect(new CopyEffect(Duration.Custom, permanentReset, event.getTargetId()), source); } return false; } diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/PharikaGodOfAffliction.java b/Mage.Sets/src/mage/sets/journeyintonyx/PharikaGodOfAffliction.java index 7b30c6509c..5f968091fc 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/PharikaGodOfAffliction.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/PharikaGodOfAffliction.java @@ -73,14 +73,13 @@ public class PharikaGodOfAffliction extends CardImpl { // As long as your devotion to black and green is less than seven, Pharika isn't a creature. Effect effect = new LoseCreatureTypeSourceEffect(new DevotionCount(ColoredManaSymbol.B, ColoredManaSymbol.G), 7); effect.setText("As long as your devotion to black and green is less than seven, Pharika isn't a creature"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // {B}{G}: Exile target creature card from a graveyard. It's owner puts a 1/1 black and green Snake enchantment creature token with deathtouch onto the battlefield. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PharikaExileEffect(), new ManaCostsImpl("{B}{G}")); Target target = new TargetCardInGraveyard(new FilterCreatureCard("a creature card from a graveyard")); ability.addTarget(target); this.addAbility(ability); - - + } public PharikaGodOfAffliction(final PharikaGodOfAffliction card) { @@ -98,7 +97,7 @@ class PharikaExileEffect extends OneShotEffect { public PharikaExileEffect() { super(Outcome.PutCreatureInPlay); staticText = "Exile target creature card from a graveyard. It's owner puts a 1/1 black and green Snake enchantment creature token with deathtouch onto the battlefield"; - } + } public PharikaExileEffect(final PharikaExileEffect effect) { super(effect); @@ -116,8 +115,8 @@ class PharikaExileEffect extends OneShotEffect { Player tokenController = game.getPlayer(targetCard.getOwnerId()); if (tokenController != null) { return new PharikaSnakeToken().putOntoBattlefield(1, game, source.getSourceId(), tokenController.getId()); - } - } + } + } } return false; } @@ -132,15 +131,13 @@ class PharikaExileEffect extends OneShotEffect { class PharikaSnakeToken extends Token { public PharikaSnakeToken() { - super("Snake", "1/1 black and green Snake enchantment creature token with deathtouch"); + super("Snake", "1/1 black and green Snake enchantment creature token with deathtouch", 1, 1); this.setOriginalExpansionSetCode("JOU"); cardType.add(CardType.ENCHANTMENT); cardType.add(CardType.CREATURE); subtype.add("Snake"); color.setBlack(true); color.setGreen(true); - power.setValue(1); - toughness.setValue(1); this.addAbility(DeathtouchAbility.getInstance()); } } diff --git a/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java index 0bb8806720..a0c75b4138 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java +++ b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java @@ -134,8 +134,8 @@ class NissaSageAnimistToken extends Token { super("Ashaya, the Awoken World", "legendary 4/4 green Elemental creature token named Ashaya, the Awoken World"); this.setOriginalExpansionSetCode("ORI"); this.getSupertype().add("Legendary"); - this.getPower().initValue(4); - this.getToughness().initValue(4); + this.getPower().modifyBaseValue(4); + this.getToughness().modifyBaseValue(4); this.color.setGreen(true); this.getSubtype().add("Elemental"); this.getCardType().add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRebirth.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRebirth.java index 320df19d47..22646d8a79 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRebirth.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/PhyrexianRebirth.java @@ -81,8 +81,8 @@ public class PhyrexianRebirth extends CardImpl { count += permanent.destroy(source.getSourceId(), game, false) ? 1 : 0; } HorrorToken horrorToken = new HorrorToken(); - horrorToken.getPower().initValue(count); - horrorToken.getToughness().initValue(count); + horrorToken.getPower().modifyBaseValue(count); + horrorToken.getToughness().modifyBaseValue(count); horrorToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/planarchaos/JeditOjanenOfEfrava.java b/Mage.Sets/src/mage/sets/planarchaos/JeditOjanenOfEfrava.java index d833505128..f798f676eb 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/JeditOjanenOfEfrava.java +++ b/Mage.Sets/src/mage/sets/planarchaos/JeditOjanenOfEfrava.java @@ -74,8 +74,8 @@ class CatWarriorToken extends Token { CatWarriorToken() { super("Cat Warrior", "2/2 green Cat Warrior creature token with forestwalk"); this.setOriginalExpansionSetCode("PLC"); - this.getPower().initValue(2); - this.getToughness().initValue(2); + this.getPower().modifyBaseValue(2); + this.getToughness().modifyBaseValue(2); this.color.setGreen(true); this.getSubtype().add("Cat"); this.getSubtype().add("Warrior"); diff --git a/Mage.Sets/src/mage/sets/planechase2012/PrimalPlasma.java b/Mage.Sets/src/mage/sets/planechase2012/PrimalPlasma.java index 4dc8803fc8..00c173a7ff 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/PrimalPlasma.java +++ b/Mage.Sets/src/mage/sets/planechase2012/PrimalPlasma.java @@ -33,7 +33,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -43,7 +42,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; import mage.constants.Zone; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; @@ -153,7 +151,9 @@ class PrimalPlasmaReplacementEffect extends ReplacementEffectImpl { game.addEffect(new GainAbilitySourceEffect(DefenderAbility.getInstance(), Duration.Custom), source); break; } - game.addEffect(new SetPowerToughnessSourceEffect(power, toughness, Duration.Custom, SubLayer.SetPT_7b), source); + permanent.getPower().modifyBaseValue(power); + permanent.getToughness().modifyBaseValue(toughness); + // game.addEffect(new SetPowerToughnessSourceEffect(power, toughness, Duration.Custom, SubLayer.SetPT_7b), source); } return false; diff --git a/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java b/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java index 53b7012a2c..2090222a81 100644 --- a/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java +++ b/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java @@ -31,11 +31,8 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.effects.common.continuous.SetPowerSourceEffect; -import mage.abilities.effects.common.continuous.SetToughnessSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -95,14 +92,14 @@ class MoltenSentryEffect extends OneShotEffect { if (controller != null && permanent != null) { if (controller.flipCoin(game)) { game.informPlayers("Heads: " + permanent.getLogName() + " enters the battlefield as a 5/2 creature with haste"); - game.addEffect(new SetPowerSourceEffect(new StaticValue(5), Duration.WhileOnBattlefield), source); - game.addEffect(new SetToughnessSourceEffect(new StaticValue(2), Duration.WhileOnBattlefield), source); + permanent.getPower().modifyBaseValue(5); + permanent.getToughness().modifyBaseValue(2); game.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield), source); return true; } else { game.informPlayers("Tails: " + permanent.getLogName() + " enters the battlefield as a 2/5 creature with defender"); - game.addEffect(new SetPowerSourceEffect(new StaticValue(2), Duration.WhileOnBattlefield), source); - game.addEffect(new SetToughnessSourceEffect(new StaticValue(5), Duration.WhileOnBattlefield), source); + permanent.getPower().modifyBaseValue(2); + permanent.getToughness().modifyBaseValue(5); game.addEffect(new GainAbilitySourceEffect(DefenderAbility.getInstance(), Duration.WhileOnBattlefield), source); return true; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SlimeMolding.java b/Mage.Sets/src/mage/sets/returntoravnica/SlimeMolding.java index d9e9e1e031..d18e1990e6 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SlimeMolding.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SlimeMolding.java @@ -80,8 +80,8 @@ class SlimeMoldingEffect extends OneShotEffect { int count = source.getManaCostsToPay().getX(); OozeToken oozeToken = new OozeToken(); - oozeToken.getPower().initValue(count); - oozeToken.getToughness().initValue(count); + oozeToken.getPower().modifyBaseValue(count); + oozeToken.getToughness().modifyBaseValue(count); oozeToken.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java index 8a2737c7da..02706e336d 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java @@ -83,8 +83,8 @@ class DevastatingSummonsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { ElementalToken token = new ElementalToken(); - token.getPower().initValue(new GetXValue().calculate(game, source, this)); - token.getToughness().initValue(new GetXValue().calculate(game, source, this)); + token.getPower().modifyBaseValue(new GetXValue().calculate(game, source, this)); + token.getToughness().modifyBaseValue(new GetXValue().calculate(game, source, this)); token.putOntoBattlefield(2, game, source.getSourceId(), source.getControllerId()); diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/GelatinousGenesis.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/GelatinousGenesis.java index 02c3b66476..195e675e3c 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/GelatinousGenesis.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/GelatinousGenesis.java @@ -80,8 +80,8 @@ class GelatinousGenesisEffect extends OneShotEffect { int count = source.getManaCostsToPay().getX(); OozeToken oozeToken = new OozeToken(); - oozeToken.getPower().initValue(count); - oozeToken.getToughness().initValue(count); + oozeToken.getPower().modifyBaseValue(count); + oozeToken.getToughness().modifyBaseValue(count); oozeToken.putOntoBattlefield(count, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java index 6ec8403ec4..8dd24f9cb2 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java @@ -72,15 +72,15 @@ class QuicksilverGargantuanApplyToPermanent extends ApplyToPermanent { @Override public Boolean apply(Game game, Permanent permanent) { - permanent.getPower().initValue(7); - permanent.getToughness().initValue(7); + permanent.getPower().modifyBaseValue(7); + permanent.getToughness().modifyBaseValue(7); return true; } @Override public Boolean apply(Game game, MageObject mageObject) { - mageObject.getPower().initValue(7); - mageObject.getToughness().initValue(7); + mageObject.getPower().modifyBaseValue(7); + mageObject.getToughness().modifyBaseValue(7); return true; } diff --git a/Mage.Sets/src/mage/sets/timespiral/Wurmcalling.java b/Mage.Sets/src/mage/sets/timespiral/Wurmcalling.java index fac4952c75..a2f6d7f420 100644 --- a/Mage.Sets/src/mage/sets/timespiral/Wurmcalling.java +++ b/Mage.Sets/src/mage/sets/timespiral/Wurmcalling.java @@ -80,8 +80,8 @@ class WurmcallingEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { int count = source.getManaCostsToPay().getX(); WurmToken token = new WurmToken(); - token.getPower().initValue(count); - token.getToughness().initValue(count); + token.getPower().modifyBaseValue(count); + token.getToughness().modifyBaseValue(count); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/PhyrexianProcessor.java b/Mage.Sets/src/mage/sets/urzassaga/PhyrexianProcessor.java index 6c5eab1115..5f93f9346f 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/PhyrexianProcessor.java +++ b/Mage.Sets/src/mage/sets/urzassaga/PhyrexianProcessor.java @@ -129,8 +129,8 @@ class PhyrexianProcessorCreateTokenEffect extends OneShotEffect { if(object != null && object instanceof Integer) { int lifePaid = (int) object; MinionToken token = new MinionToken(); - token.getPower().initValue(lifePaid); - token.getToughness().initValue(lifePaid); + token.getPower().modifyBaseValue(lifePaid); + token.getToughness().modifyBaseValue(lifePaid); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java index 634eee84bb..fcd158d5dc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java @@ -225,6 +225,8 @@ public class MorphTest extends CardTestPlayerBase { */ @Test public void testPineWalkerWithUnboostEffect() { + // Morph {4}{G} + // Whenever Pine Walker or another creature you control is turned face up, untap that creature. addCard(Zone.HAND, playerA, "Pine Walker"); addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); @@ -641,24 +643,25 @@ public class MorphTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Pine Walker", 5, 5); assertTapped("Pine Walker", false); } - + /** - * Reflector Mage bouncing a creature that can be played as a morph should not prevent the card - * from being replayed as a morph. Morph creatures are nameless. - * - * Reported bug: - * Face-up morph creatures that are bounced by Reflector Mage should be able to be replayed as morphs - * without the "until the next turn" restriction." + * Reflector Mage bouncing a creature that can be played as a morph should + * not prevent the card from being replayed as a morph. Morph creatures are + * nameless. + * + * Reported bug: Face-up morph creatures that are bounced by Reflector Mage + * should be able to be replayed as morphs without the "until the next turn" + * restriction." */ @Test public void testReflectorMageBouncesFaceupCreatureReplayAsMorph() { - - // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. + + // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. // That creature's owner can't cast spells with the same name as that creature until your next turn. addCard(Zone.HAND, playerA, "Reflector Mage"); // 2/3 addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.BATTLEFIELD, playerA, "Island", 2); - + //Tap: Add {G}, {U}, or {R} to your mana pool. // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) // When Rattleclaw Mystic is turned face up, add {G}{U}{R} to your mana pool. @@ -666,36 +669,37 @@ public class MorphTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Forest"); addCard(Zone.BATTLEFIELD, playerB, "Island"); addCard(Zone.BATTLEFIELD, playerB, "Mountain"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reflector Mage"); addTarget(playerA, "Rattleclaw Mystic"); - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rattleclaw Mystic"); setChoice(playerB, "Yes"); // cast it face down as 2/2 creature setStopAt(2, PhaseStep.BEGIN_COMBAT); - + execute(); - - assertPermanentCount(playerA, "Reflector Mage", 1); - assertPermanentCount(playerB, "Rattleclaw Mystic", 0); + + assertPermanentCount(playerA, "Reflector Mage", 1); + assertPermanentCount(playerB, "Rattleclaw Mystic", 0); assertHandCount(playerB, "Rattleclaw Mystic", 0); // should have been replayed assertPermanentCount(playerB, "", 1); // Rattleclaw played as a morph } - + /** - * Reflector Mage bouncing a creature that can be played as a morph should not prevent the card - * from being replayed as a morph. Morph creatures are nameless. - * - * Reported bug: - * Face-up morph creatures that are bounced by Reflector Mage should be able to be replayed as morphs - * without the "until the next turn" restriction." - * + * Reflector Mage bouncing a creature that can be played as a morph should + * not prevent the card from being replayed as a morph. Morph creatures are + * nameless. + * + * Reported bug: Face-up morph creatures that are bounced by Reflector Mage + * should be able to be replayed as morphs without the "until the next turn" + * restriction." + * * Testing bouncing a face-down creature played next turn face-up. */ @Test public void testReflectorMageBouncesMorphCreatureReplayAsFaceup() { - + //Tap: Add {G}, {U}, or {R} to your mana pool. // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) // When Rattleclaw Mystic is turned face up, add {G}{U}{R} to your mana pool. @@ -703,28 +707,28 @@ public class MorphTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.BATTLEFIELD, playerA, "Mountain"); - - // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. + + // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand. // That creature's owner can't cast spells with the same name as that creature until your next turn. - addCard(Zone.HAND, playerB, "Reflector Mage"); // 2/3 - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - + addCard(Zone.HAND, playerB, "Reflector Mage"); // 2/3 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rattleclaw Mystic"); setChoice(playerA, "Yes"); // cast it face down as 2/2 creature - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Reflector Mage"); addTarget(playerB, ""); - + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Rattleclaw Mystic"); setChoice(playerA, "No"); // cast it face down as 2/2 creature setStopAt(3, PhaseStep.BEGIN_COMBAT); - + execute(); - - assertPermanentCount(playerB, "Reflector Mage", 1); - assertPermanentCount(playerA, "Rattleclaw Mystic", 1); + + assertPermanentCount(playerB, "Reflector Mage", 1); + assertPermanentCount(playerA, "Rattleclaw Mystic", 1); assertHandCount(playerA, "Rattleclaw Mystic", 0); // should have been replayed faceup } } 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 23e9d33060..501f2afa37 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 @@ -381,7 +381,9 @@ public class TestPlayer implements Player { @Override public boolean priority(Game game) { int numberOfActions = actions.size(); - for (PlayerAction action : actions) { + List tempActions = new ArrayList<>(); + tempActions.addAll(actions); + for (PlayerAction action : tempActions) { if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { if (action.getAction().startsWith("activate:")) { @@ -478,7 +480,7 @@ public class TestPlayer implements Player { int turns = Integer.parseUnsignedInt(groups[1].substring(6)); game.rollbackTurns(turns); actions.remove(action); - break; + return true; } } } diff --git a/Mage/src/main/java/mage/MageInt.java b/Mage/src/main/java/mage/MageInt.java index c16f926820..bb0fb75f82 100644 --- a/Mage/src/main/java/mage/MageInt.java +++ b/Mage/src/main/java/mage/MageInt.java @@ -24,8 +24,7 @@ * 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; import java.io.Serializable; @@ -49,15 +48,28 @@ public class MageInt implements Serializable, Copyable { }; protected int baseValue; + protected int baseValueModified; + protected int boostedValue; protected String cardValue = ""; public MageInt(int value) { this.baseValue = value; + this.baseValueModified = baseValue; + this.boostedValue = baseValue; this.cardValue = Integer.toString(value); } public MageInt(int baseValue, String cardValue) { this.baseValue = baseValue; + this.baseValueModified = baseValue; + this.boostedValue = baseValue; + this.cardValue = cardValue; + } + + public MageInt(int baseValue, int baseValueModified, int boostedValue, String cardValue) { + this.baseValue = baseValue; + this.baseValueModified = baseValueModified; + this.boostedValue = boostedValue; this.cardValue = cardValue; } @@ -66,24 +78,37 @@ public class MageInt implements Serializable, Copyable { if (this == EmptyMageInt) { return this; } - return new MageInt(baseValue, cardValue); + return new MageInt(baseValue, baseValueModified, boostedValue, cardValue); } - public int getValue() { + public int getBaseValue() { return baseValue; } - public void initValue(int value) { - this.baseValue = value; + public int getBaseValueModified() { + return baseValueModified; + } + + public int getValue() { + return boostedValue; + } + + public void modifyBaseValue(int value) { + this.baseValueModified = value; + this.boostedValue = value; this.cardValue = Integer.toString(value); } public void setValue(int value) { - this.baseValue = value; + this.boostedValue = value; } public void boostValue(int amount) { - this.baseValue += amount; + this.boostedValue += amount; + } + + public void resetToBaseValue() { + this.boostedValue = this.baseValueModified; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 1e7ccd2a5e..4e80d92298 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -47,13 +47,12 @@ import mage.game.Game; import mage.game.permanent.Permanent; /** - * This effect lets the card be a 2/2 face-down creature, with no text, - * no name, no subtypes, and no mana cost, if it's face down on the battlefield. - * And it adds the a TurnFaceUpAbility ability. - * + * This effect lets the card be a 2/2 face-down creature, with no text, no name, + * no subtypes, and no mana cost, if it's face down on the battlefield. And it + * adds the a TurnFaceUpAbility ability. + * * @author LevelX2 */ - public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implements SourceEffect { public enum FaceDownType { @@ -65,15 +64,15 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen protected int zoneChangeCounter; protected Ability turnFaceUpAbility = null; - protected MageObjectReference objectReference= null; + protected MageObjectReference objectReference = null; protected boolean foundPermanent; protected FaceDownType faceDownType; - public BecomesFaceDownCreatureEffect(Duration duration, FaceDownType faceDownType){ + public BecomesFaceDownCreatureEffect(Duration duration, FaceDownType faceDownType) { this(null, null, duration, faceDownType); } - public BecomesFaceDownCreatureEffect(Costs turnFaceUpCosts, FaceDownType faceDownType){ + public BecomesFaceDownCreatureEffect(Costs turnFaceUpCosts, FaceDownType faceDownType) { this(turnFaceUpCosts, null, faceDownType); } @@ -97,7 +96,6 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen this.faceDownType = faceDownType; } - public BecomesFaceDownCreatureEffect(final BecomesFaceDownCreatureEffect effect) { super(effect); this.zoneChangeCounter = effect.zoneChangeCounter; @@ -147,11 +145,11 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen } else { permanent = game.getPermanent(source.getSourceId()); } - + if (permanent != null && permanent.isFaceDown(game)) { if (!foundPermanent) { foundPermanent = true; - switch(faceDownType) { + switch (faceDownType) { case MANIFESTED: case MANUAL: // sets manifested image permanent.setManifested(true); @@ -184,11 +182,9 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen if (ability.getWorksFaceDown()) { ability.setRuleVisible(false); continue; - } else { - if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { - if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) { - continue; - } + } else if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { + if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) { + continue; } } abilitiesToRemove.add(ability); @@ -200,14 +196,12 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen break; case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { - permanent.getPower().setValue(2); - permanent.getToughness().setValue(2); +// permanent.getPower().setValue(2); +// permanent.getToughness().setValue(2); } } - } else { - if (duration.equals(Duration.Custom) && foundPermanent == true) { - discard(); - } + } else if (duration.equals(Duration.Custom) && foundPermanent == true) { + discard(); } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java index 9ced7947bb..31655da573 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java @@ -115,7 +115,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl { if (staticText != null && !staticText.isEmpty()) { return staticText; } - if (mode == null || mode.getTargets().size() == 0) { + if (mode == null || mode.getTargets().isEmpty()) { return "no target"; } Target target = mode.getTargets().get(0); diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index f5726d50f7..0ce6b3c490 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -208,7 +208,6 @@ class FlashbackEffect extends OneShotEffect { if (!game.isSimulation()) { game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName()); } - // spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells if (controller.cast(spellAbility, game, false)) { ContinuousEffect effect = new FlashbackReplacementEffect(); effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()))); diff --git a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java index afaf4246c2..c77fa4c893 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java @@ -295,8 +295,8 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost } public static void setPermanentToFaceDownCreature(MageObject mageObject) { - mageObject.getPower().initValue(2); - mageObject.getToughness().initValue(2); + mageObject.getPower().modifyBaseValue(2); + mageObject.getToughness().modifyBaseValue(2); mageObject.getAbilities().clear(); mageObject.getColor(null).setColor(new ObjectColor()); mageObject.setName(""); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index 35c9e779d1..6fd8f34574 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -61,6 +61,8 @@ public class PermanentCard extends PermanentImpl { } private void init(Card card, Game game) { + power = card.getPower().copy(); + toughness = card.getToughness().copy(); copyFromCard(card); // if temporary added abilities to the spell/card exist, you need to add it to the permanent derived from that card Abilities otherAbilities = game.getState().getAllOtherAbilities(card.getId()); @@ -94,6 +96,8 @@ public class PermanentCard extends PermanentImpl { // when the permanent is reset, copy all original values from the card // must copy card each reset so that the original values don't get modified copyFromCard(card); + power.resetToBaseValue(); + toughness.resetToBaseValue(); super.reset(game); } @@ -115,8 +119,6 @@ public class PermanentCard extends PermanentImpl { this.cardType.addAll(card.getCardType()); this.color = card.getColor(null).copy(); this.manaCost = card.getManaCost().copy(); - this.power = card.getPower().copy(); - this.toughness = card.getToughness().copy(); if (card instanceof PermanentCard) { this.maxLevelCounters = ((PermanentCard) card).maxLevelCounters; } @@ -232,6 +234,8 @@ public class PermanentCard extends PermanentImpl { @Override public boolean turnFaceUp(Game game, UUID playerId) { if (super.turnFaceUp(game, playerId)) { + power.modifyBaseValue(power.getBaseValue()); + toughness.modifyBaseValue(toughness.getBaseValue()); setManifested(false); setMorphed(false); return true; @@ -241,7 +245,7 @@ public class PermanentCard extends PermanentImpl { @Override public void adjustTargets(Ability ability, Game game) { - if (this.isTransformed()) { + if (this.isTransformed() && card.getSecondCardFace() != null) { card.getSecondCardFace().adjustTargets(ability, game); } else { card.adjustTargets(ability, game); @@ -250,7 +254,7 @@ public class PermanentCard extends PermanentImpl { @Override public void adjustCosts(Ability ability, Game game) { - if (this.isTransformed()) { + if (this.isTransformed() && card.getSecondCardFace() != null) { card.getSecondCardFace().adjustCosts(ability, game); } else { card.adjustCosts(ability, game); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index d8e02c5e12..2c649bf5f8 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -82,8 +82,8 @@ public class PermanentToken extends PermanentImpl { } this.cardType = token.getCardType(); this.color = token.getColor(game).copy(); - this.power.initValue(token.getPower().getValue()); - this.toughness.initValue(token.getToughness().getValue()); + this.power.modifyBaseValue(token.getPower().getBaseValueModified()); + this.toughness.modifyBaseValue(token.getToughness().getBaseValueModified()); this.supertype = token.getSupertype(); this.subtype = token.getSubtype(); } diff --git a/Mage/src/main/java/mage/game/permanent/token/Token.java b/Mage/src/main/java/mage/game/permanent/token/Token.java index 51c02248ca..91e719ce57 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/Token.java @@ -81,11 +81,11 @@ public class Token extends MageObjectImpl { this.name = name; this.description = description; } - + public Token(String name, String description, int power, int toughness) { this(name, description); - this.power.setValue(power); - this.toughness.setValue(toughness); + this.power.modifyBaseValue(power); + this.toughness.modifyBaseValue(toughness); } public Token(String name, String description, ObjectColor color, List subtype, int power, int toughness, Abilities abilities) { @@ -93,8 +93,8 @@ public class Token extends MageObjectImpl { this.cardType.add(CardType.CREATURE); this.color = color.copy(); this.subtype = subtype; - this.power.setValue(power); - this.toughness.setValue(toughness); + this.power.modifyBaseValue(power); + this.toughness.modifyBaseValue(toughness); if (abilities != null) { this.abilities = abilities.copy(); } @@ -217,11 +217,11 @@ public class Token extends MageObjectImpl { } return false; } - + public void setPower(int power) { this.power.setValue(power); } - + public void setToughness(int toughness) { this.toughness.setValue(toughness); } @@ -264,17 +264,13 @@ public class Token extends MageObjectImpl { if (availableImageSetCodes.size() > 0) { if (availableImageSetCodes.contains(code)) { setOriginalExpansionSetCode(code); - } else { - // we should not set random set if appropriate set is already used - if (getOriginalExpansionSetCode() == null || getOriginalExpansionSetCode().isEmpty() - || !availableImageSetCodes.contains(getOriginalExpansionSetCode())) { - setOriginalExpansionSetCode(availableImageSetCodes.get(new Random().nextInt(availableImageSetCodes.size()))); - } - } - } else { - if (getOriginalExpansionSetCode() == null || getOriginalExpansionSetCode().isEmpty()) { - setOriginalExpansionSetCode(code); + } else // we should not set random set if appropriate set is already used + if (getOriginalExpansionSetCode() == null || getOriginalExpansionSetCode().isEmpty() + || !availableImageSetCodes.contains(getOriginalExpansionSetCode())) { + setOriginalExpansionSetCode(availableImageSetCodes.get(new Random().nextInt(availableImageSetCodes.size()))); } + } else if (getOriginalExpansionSetCode() == null || getOriginalExpansionSetCode().isEmpty()) { + setOriginalExpansionSetCode(code); } } diff --git a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java index 8281818ac2..b5452b3ff1 100644 --- a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java @@ -108,9 +108,9 @@ public class CopyTokenFunction implements Function { ability.setSourceId(target.getId()); target.addAbility(ability); } - // Needed to do it this way because only the cardValue does not include the increased value from cards like "Intangible Virtue" will be copied. - target.getPower().initValue(Integer.parseInt(sourceObj.getPower().toString())); - target.getToughness().initValue(Integer.parseInt(sourceObj.getToughness().toString())); + + target.getPower().modifyBaseValue(sourceObj.getPower().getBaseValueModified()); + target.getToughness().modifyBaseValue(sourceObj.getToughness().getBaseValueModified()); return target; } From 48dfdafe1e23003ac01aadca3be8c20a5420d52b Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Fri, 5 Aug 2016 17:10:25 -0500 Subject: [PATCH 06/24] Implemented Celestial Sword from Iceage and ME4 --- .../src/mage/sets/iceage/CelestialSword.java | 74 +++++++++++++++++++ .../sets/masterseditioniv/CelestialSword.java | 54 ++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/iceage/CelestialSword.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java diff --git a/Mage.Sets/src/mage/sets/iceage/CelestialSword.java b/Mage.Sets/src/mage/sets/iceage/CelestialSword.java new file mode 100644 index 0000000000..3c96903178 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/CelestialSword.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.iceage; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author anonymous + */ +public class CelestialSword extends CardImpl { + + public CelestialSword(UUID ownerId) { + super(ownerId, 289, "Celestial Sword", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{6}"); + this.expansionSetCode = "ICE"; + + // {3}, {tap}: Target creature you control gets +3/+3 until end of turn. Its controller sacrifices it at the beginning of the next end step. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(3, 3, Duration.EndOfTurn), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent()); + ability.addEffect(new GainAbilityTargetEffect(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new SacrificeSourceEffect()),Duration.EndOfTurn)); + this.addAbility(ability); + } + + public CelestialSword(final CelestialSword card) { + super(card); + } + + @Override + public CelestialSword copy() { + return new CelestialSword(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java b/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java new file mode 100644 index 0000000000..930f5e818a --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.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.masterseditioniv; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author anonymous + */ +public class CelestialSword extends mage.sets.iceage.CelestialSword { + + public CelestialSword(UUID ownerId) { + super(ownerId); + this.cardNumber = "188"; + this.expansionSetCode = "ME4"; + this.rarity = Rarity.UNCOMMON; + } + + public CelestialSword(final CelestialSword card) { + super(card); + } + + @Override + public CelestialSword copy() { + return new CelestialSword(this); + } +} From cb591e10ef1a2f884e4c61162b2390796a86b171 Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Fri, 5 Aug 2016 17:11:27 -0500 Subject: [PATCH 07/24] Fixed author comment --- Mage.Sets/src/mage/sets/iceage/CelestialSword.java | 2 +- Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/iceage/CelestialSword.java b/Mage.Sets/src/mage/sets/iceage/CelestialSword.java index 3c96903178..9acf2d7d88 100644 --- a/Mage.Sets/src/mage/sets/iceage/CelestialSword.java +++ b/Mage.Sets/src/mage/sets/iceage/CelestialSword.java @@ -47,7 +47,7 @@ import mage.target.common.TargetControlledCreaturePermanent; /** * - * @author anonymous + * @author choiseul11 */ public class CelestialSword extends CardImpl { diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java b/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java index 930f5e818a..7b6cede212 100644 --- a/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java +++ b/Mage.Sets/src/mage/sets/masterseditioniv/CelestialSword.java @@ -32,7 +32,7 @@ import mage.constants.Rarity; /** * - * @author anonymous + * @author choiseul11 */ public class CelestialSword extends mage.sets.iceage.CelestialSword { From 57995f893ea97095bf9994a628d03fb1505df5f9 Mon Sep 17 00:00:00 2001 From: drmDev Date: Fri, 5 Aug 2016 18:17:10 -0400 Subject: [PATCH 08/24] Clergy of Holy Nimbus tests --- .../ClergyOfTheHolyNimbusTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java new file mode 100644 index 0000000000..ad2ee133e4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ClergyOfTheHolyNimbusTest.java @@ -0,0 +1,68 @@ +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class ClergyOfTheHolyNimbusTest extends CardTestPlayerBase { + + @Test + public void testBasicRegeneration() { + + // If Clergy of the Holy Nimbus would be destroyed, regenerate it. + // {1}: Clergy of the Holy Nimbus can't be regenerated this turn. Only any opponent may activate this ability. + addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 + addCard(Zone.HAND, playerA, "Doom Blade"); // {1}{B} destroy target non-black creature + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Clergy of the Holy Nimbus"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Doom Blade", 1); + assertPermanentCount(playerB, "Clergy of the Holy Nimbus", 1); + } + + @Test + public void testCannotBeRegeneratedSpell() { + + // If Clergy of the Holy Nimbus would be destroyed, regenerate it. + // {1}: Clergy of the Holy Nimbus can't be regenerated this turn. Only any opponent may activate this ability. + addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 + addCard(Zone.HAND, playerA, "Wrath of God"); // {2}{W}{W} destroy all creatures, they cannot be regenerated + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wrath of God", 1); + assertGraveyardCount(playerB, "Clergy of the Holy Nimbus", 1); + } + + // in game testing works correctly - not sure if the ability is being activated or not here. + @Ignore + @Test + public void testOpponentPaysOneToNotAllowRegeneration() { + + // If Clergy of the Holy Nimbus would be destroyed, regenerate it. + // {1}: Clergy of the Holy Nimbus can't be regenerated this turn. Only any opponent may activate this ability. + addCard(Zone.BATTLEFIELD, playerB, "Clergy of the Holy Nimbus"); // {W} 1/1 + addCard(Zone.HAND, playerA, "Doom Blade"); // {1}{B} destroy target non-black creature + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}:"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Doom Blade", "Clergy of the Holy Nimbus"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Doom Blade", 1); + assertGraveyardCount(playerB, "Clergy of the Holy Nimbus", 1); + } +} From b91ef8b3852b3ee1dcb04de30c726e1aad17a757 Mon Sep 17 00:00:00 2001 From: Quercitron Date: Sat, 6 Aug 2016 03:15:54 +0300 Subject: [PATCH 09/24] * Thirsting Axe - Fix check if equipped creature dealt combat damage to creature (fixes #2145). --- .../mage/sets/eldritchmoon/ThirstingAxe.java | 70 ++++++++++++++----- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ThirstingAxe.java b/Mage.Sets/src/mage/sets/eldritchmoon/ThirstingAxe.java index d54f8ad59e..d80276c5b8 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/ThirstingAxe.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ThirstingAxe.java @@ -27,8 +27,12 @@ */ package mage.sets.eldritchmoon; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -36,7 +40,6 @@ import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.AttachedCondition; -import mage.abilities.condition.common.WatcherCondition; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.SacrificeEquippedEffect; @@ -50,6 +53,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.watchers.Watcher; + /** * * @author Quercitron @@ -67,12 +71,12 @@ public class ThirstingAxe extends CardImpl { // At the beginning of your end step, if equipped creature didn't deal combat damage to a creature this turn, sacrifice it. TriggeredAbility ability = new BeginningOfYourEndStepTriggeredAbility(new SacrificeEquippedEffect(), false); Condition condition = new CompoundCondition( - new AttachedCondition(), - new InvertCondition(new WatcherCondition(CombatDamageToCreatureByEquippedWatcher.BASIC_KEY, WatcherScope.CARD))); + AttachedCondition.getInstance(), + new InvertCondition(new EquippedDealtCombatDamageToCreatureCondition())); String triggeredAbilityText = "At the beginning of your end step, if equipped creature " + "didn't deal combat damage to a creature this turn, sacrifice it."; ConditionalTriggeredAbility sacrificeTriggeredAbility = new ConditionalTriggeredAbility(ability, condition, triggeredAbilityText); - this.addAbility(sacrificeTriggeredAbility, new CombatDamageToCreatureByEquippedWatcher()); + this.addAbility(sacrificeTriggeredAbility, new CombatDamageToCreatureWatcher()); // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); @@ -88,34 +92,62 @@ public class ThirstingAxe extends CardImpl { } } -class CombatDamageToCreatureByEquippedWatcher extends Watcher { +class EquippedDealtCombatDamageToCreatureCondition implements Condition { - public final static String BASIC_KEY = "CombatDamageToCreatureByEquippedWatcher"; - - public CombatDamageToCreatureByEquippedWatcher() { - super(BASIC_KEY, WatcherScope.CARD); + @Override + public boolean apply(Game game, Ability source) { + Permanent equipment = game.getPermanent(source.getSourceId()); + if (equipment != null && equipment.getAttachedTo() != null) { + CombatDamageToCreatureWatcher watcher = + (CombatDamageToCreatureWatcher) game.getState().getWatchers().get(CombatDamageToCreatureWatcher.BASIC_KEY); + return watcher.dealtDamage(equipment.getAttachedTo(), equipment.getAttachedToZoneChangeCounter(), game); + } + return false; } - public CombatDamageToCreatureByEquippedWatcher(final CombatDamageToCreatureByEquippedWatcher watcher) { +} + +class CombatDamageToCreatureWatcher extends Watcher { + + // which objects dealt combat damage to creature during the turn + public final Set dealtCombatDamageToCreature; + + public final static String BASIC_KEY = "CombatDamageToCreatureWatcher"; + + public CombatDamageToCreatureWatcher() { + super(BASIC_KEY, WatcherScope.GAME); + dealtCombatDamageToCreature = new HashSet<>(); + } + + public CombatDamageToCreatureWatcher(final CombatDamageToCreatureWatcher watcher) { super(watcher); + dealtCombatDamageToCreature = new HashSet<>(watcher.dealtCombatDamageToCreature); } @Override - public CombatDamageToCreatureByEquippedWatcher copy() { - return new CombatDamageToCreatureByEquippedWatcher(this); + public CombatDamageToCreatureWatcher copy() { + return new CombatDamageToCreatureWatcher(this); } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - Permanent equipment = game.getPermanent(this.getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - if (equipment.getAttachedTo().equals(event.getSourceId())) { - if (((DamagedCreatureEvent)event).isCombatDamage()) { - condition = true; - } - } + if (((DamagedCreatureEvent) event).isCombatDamage()) { + MageObjectReference damageSource = new MageObjectReference(event.getSourceId(), game); + dealtCombatDamageToCreature.add(damageSource); } } } + + @Override + public void reset() { + super.reset(); + dealtCombatDamageToCreature.clear(); + } + + public boolean dealtDamage(UUID objectId, int zoneChangeCounter, Game game) { + MageObjectReference reference = new MageObjectReference(objectId, zoneChangeCounter, game); + return dealtCombatDamageToCreature.contains(reference); + } + } From 895e65a42c24fe0334eb984e46d6934f5a517c41 Mon Sep 17 00:00:00 2001 From: drmDev Date: Fri, 5 Aug 2016 21:29:37 -0400 Subject: [PATCH 10/24] Kusari-Gama test confirms bug for #2154 --- .../abilities/equipped/KusariGamaTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java new file mode 100644 index 0000000000..5b9a376270 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java @@ -0,0 +1,48 @@ +package org.mage.test.cards.abilities.equipped; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class KusariGamaTest extends CardTestPlayerBase { + + // reported bug: trigger occurs but no damage is dealt + @Test + public void testTriggeredAbilityDealsDamage() { + + // Kusari-Gama - Artifact Equipment - Equip {3} + // Equipped creature has "2: This creature gets +1/+0 until end of turn." + // Whenever equipped creature deals damage to a blocking creature, Kusari-Gama deals that much damage to each other creature defending player controls. + addCard(Zone.BATTLEFIELD, playerA, "Kusari-Gama"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Sylvan Advocate"); // 2/3 vigilance {1}{G} + + addCard(Zone.BATTLEFIELD, playerB, "Wall of Omens"); // 0/4 {1}{W} + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); // 2/2 {1}{W} + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // 3/3 {3}{R} + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Equip {3}", "Sylvan Advocate"); + attack(1, playerA, "Sylvan Advocate"); + block(1, playerB, "Wall of Omens", "Sylvan Advocate"); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerA, "Kusari-Gama", 1); + assertPermanentCount(playerB, "Wall of Omens", 1); + assertPermanentCount(playerB, "Hill Giant", 1); + + Permanent wallPerm = getPermanent("Wall of Omens", playerB); + Permanent giantPerm = getPermanent("Hill Giant", playerB); + Assert.assertEquals("Wall of Omens should have 2 damage dealt to it", 2, wallPerm.getDamage()); + Assert.assertEquals("Hill Giant should have 2 damage dealt to it", 2, giantPerm.getDamage()); + + assertGraveyardCount(playerB, "Silvercoat Lion", 2); + } +} From f340c3da85015c7dc1761ded9fd2dd328e7aaeea Mon Sep 17 00:00:00 2001 From: drmDev Date: Fri, 5 Aug 2016 21:52:04 -0400 Subject: [PATCH 11/24] test confirming undying bug for #2148 --- .../cards/abilities/keywords/UndyingTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 61b401429c..1d004f808a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -76,6 +76,7 @@ public class UndyingTest extends CardTestPlayerBase { execute(); assertPermanentCount(playerA, "Elite Vanguard", 1); + assertCounterCount(playerA, "Elite Vanguard", CounterType.P1P1, 1); assertPowerToughness(playerA, "Elite Vanguard", 3, 2); } @@ -112,6 +113,7 @@ public class UndyingTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Lightning Bolt", 1); assertPermanentCount(playerB, "Strangleroot Geist", 0); assertPermanentCount(playerA, "Strangleroot Geist", 1); + assertCounterCount(playerA, "Strangleroot Geist", CounterType.P1P1, 1); assertPowerToughness(playerA, "Strangleroot Geist", 3, 2); } @@ -211,6 +213,7 @@ public class UndyingTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Silvercoat Lion", 1); assertPermanentCount(playerA, "Mikaeus, the Unhallowed", 1); + assertCounterCount(playerA, "Silvercoat Lion", CounterType.P1P1, 1); assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); } @@ -271,5 +274,45 @@ public class UndyingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Tatterkite", 3, 2); } + + /** + * I stole my opponents Vorapede using Simic Manipulator and shortly after someone played Wrath of God. + * Instead of returning to my opponent's board, Vorapede came back under my control. + * The rules text for Undying states that it should return under its owner's control, not its controller's. + */ + @Test + public void testUndyingCreatureReturnsUnderOwnersControl() { + + // Creature — Insect + // Vigilance, trample + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.BATTLEFIELD, playerB, "Vorapede"); // {2}{G}{G}{G} 5/4 + addCard(Zone.HAND, playerB, "Doom Blade"); // {1}{B} destroy target non-black creature + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + // {2}{R} sorcery - Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. + addCard(Zone.HAND, playerA, "Act of Treason"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // playerA takes control of Vorapede from playerB + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Act of Treason", "Vorapede"); + attack(1, playerA, "Vorapede"); + // playerB kills Vorapede under the control of playerA now + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Doom Blade", "Vorapede"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 15); + assertGraveyardCount(playerA, "Act of Treason", 1); + assertGraveyardCount(playerB, "Doom Blade", 1); + + // Vorapede should return under control of playerA, not playerB + assertPermanentCount(playerA, "Vorapede", 1); + assertPermanentCount(playerB, "Vorapede", 0); + assertCounterCount(playerA, "Vorapede", CounterType.P1P1, 1); + assertPowerToughness(playerA, "Vorapede", 6, 5); + } } From 33e0f2cdde8b633bb1b51036617e216ef77c0bf6 Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Fri, 5 Aug 2016 20:52:11 -0500 Subject: [PATCH 12/24] Implemented Murderous Betrayal --- .../sets/eighthedition/MurderousBetrayal.java | 121 ++++++++++++++++++ .../mage/sets/nemesis/MurderousBetrayal.java | 52 ++++++++ 2 files changed, 173 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java create mode 100644 Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java diff --git a/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java new file mode 100644 index 0000000000..157365df20 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java @@ -0,0 +1,121 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.eighthedition; + +import java.util.UUID; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author anonymous + */ +public class MurderousBetrayal extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); + + static { + filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + } + + public MurderousBetrayal(UUID ownerId) { + super(ownerId, 147, "Murderous Betrayal", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{B}{B}{B}"); + this.expansionSetCode = "8ED"; + + // {B}{B}, Pay half your life, rounded up: Destroy target nonblack creature. It can't be regenerated. + Effect effect = new DestroyTargetEffect(true); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new MurderousBetrayalCost()); + ability.addCost(new ManaCostsImpl("{B}{B}")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public MurderousBetrayal(final MurderousBetrayal card) { + super(card); + } + + @Override + public MurderousBetrayal copy() { + return new MurderousBetrayal(this); + } +} + +class MurderousBetrayalCost extends CostImpl { + + MurderousBetrayalCost() { + this.text = "Pay half your life, rounded up"; + } + + MurderousBetrayalCost(MurderousBetrayalCost cost) { + super(cost); + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + return controller != null && !(controller.getLife() > 0 && !controller.canPayLifeCost()); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + int currentLife = controller.getLife(); + int lifeToPay = (currentLife + currentLife % 2) / 2; // Divide by two and round up. + if (lifeToPay < 0) { + this.paid = true; + } else { + this.paid = (controller.loseLife(lifeToPay, game) == lifeToPay); + } + return this.paid; + } + return false; + } + + @Override + public MurderousBetrayalCost copy() { + return new MurderousBetrayalCost(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java b/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java new file mode 100644 index 0000000000..807a023430 --- /dev/null +++ b/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.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.nemesis; + +import java.util.UUID; + +/** + * + * @author anonymous + */ +public class MurderousBetrayal extends mage.sets.eighthedition.MurderousBetrayal { + + public MurderousBetrayal(UUID ownerId) { + super(ownerId); + this.cardNumber = "61"; + this.expansionSetCode = "NEM"; + } + + public MurderousBetrayal(final MurderousBetrayal card) { + super(card); + } + + @Override + public MurderousBetrayal copy() { + return new MurderousBetrayal(this); + } +} From adf6575eb2bce603c944fdb6ad4007053e9dd52d Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Fri, 5 Aug 2016 22:02:56 -0500 Subject: [PATCH 13/24] Fixed author comment on Murderous Betrayal --- Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java | 2 +- Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java index 157365df20..c9b1f66b85 100644 --- a/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java +++ b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java @@ -50,7 +50,7 @@ import mage.target.common.TargetCreaturePermanent; /** * - * @author anonymous + * @author choiseul11 */ public class MurderousBetrayal extends CardImpl { diff --git a/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java b/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java index 807a023430..0b29c06779 100644 --- a/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java +++ b/Mage.Sets/src/mage/sets/nemesis/MurderousBetrayal.java @@ -31,7 +31,7 @@ import java.util.UUID; /** * - * @author anonymous + * @author choiseul11 */ public class MurderousBetrayal extends mage.sets.eighthedition.MurderousBetrayal { From 3f6792bffe14854fbce4dc2c80f5ae07e9a5118c Mon Sep 17 00:00:00 2001 From: drmDev Date: Sat, 6 Aug 2016 00:24:44 -0400 Subject: [PATCH 14/24] escalate test with spell queller interaction confirms bug #2143 --- .../abilities/keywords/EscalateTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java index 8c94982111..34db143d05 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EscalateTest.java @@ -115,5 +115,45 @@ public class EscalateTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Gaddock Teeg", 1); assertLife(playerB, 17); } + + @Test + public void testSpellQuellerInteraction_ThreeCMC_ThreeModes() { + + // {1}{W}{U} Flash Flying 2/3 Spirit + // When Spell Queller enters the battlefield, exile target spell with converted mana cost 4 or less. + // When Spell Queller leaves the battlefield, the exiled card's owner may cast that card without paying its mana cost. + addCard(Zone.HAND, playerB, "Spell Queller"); + addCard(Zone.BATTLEFIELD, playerB, "Wall of Omens"); // {1}{W} 0/4 + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + + // Escalate {1} (Pay this cost for each mode chosen beyond the first.) + // Choose one or more — + // * Target player discards all the cards in his or her hand, then draws that many cards. + // * Collective Defiance deals 4 damage to target creature. + // * Collective Defiance deals 3 damage to target opponent. + addCard(Zone.HAND, playerA, "Collective Defiance"); // {1}{R}{R} sorcery + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collective Defiance", "mode=2Wall of Omens"); + setModeChoice(playerA, "1"); // opponent discards hand and draws that many + setModeChoice(playerA, "2"); // deal 4 dmg to target creature (Wall of Omens) + setModeChoice(playerA, "3"); // deal 3 dmg to opponent + addTarget(playerA, playerB); // mode 1 + addTarget(playerA, playerB); // mode 3 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Spell Queller"); + addTarget(playerB, "Collective Defiance"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Spell Queller", 1); + assertHandCount(playerA, "Collective Defiance", 0); + assertExileCount("Collective Defiance", 1); + assertGraveyardCount(playerA, "Collective Defiance", 0); + assertPermanentCount(playerB, "Wall of Omens", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + } } From e2fdcb8aa35918189c60d4ad1ef0afe61759bd0b Mon Sep 17 00:00:00 2001 From: Will Hall Date: Fri, 5 Aug 2016 22:34:46 -0700 Subject: [PATCH 15/24] Use "his or her" rather than "his" --- .../main/java/mage/server/tournament/TournamentController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java index 7c43702d5c..0d69cf257a 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java @@ -294,7 +294,7 @@ public class TournamentController { TournamentPlayer player = tournament.getPlayer(playerId); if (player != null && !player.hasQuit()) { tournamentSessions.get(playerId).submitDeck(deck); - ChatManager.getInstance().broadcast(chatId, "", player.getPlayer().getLogName() + " has submitted his tournament deck", MessageColor.BLACK, true, MessageType.STATUS, SoundToPlay.PlayerSubmittedDeck); + ChatManager.getInstance().broadcast(chatId, "", player.getPlayer().getLogName() + " has submitted his or her tournament deck", MessageColor.BLACK, true, MessageType.STATUS, SoundToPlay.PlayerSubmittedDeck); } } } From 9df373e8f36296dd382af280376ac1bfb995c200 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Aug 2016 11:33:24 +0200 Subject: [PATCH 16/24] * Fixed Leech Bonder -1/-1 Tokens don't kill off enemy creatures (fixes #1975). --- .../src/mage/sets/shadowmoor/LeechBonder.java | 13 +++-- .../abilities/keywords/ProliferateTest.java | 48 +++++++++---------- .../oneshot/counter/MovingCounterTest.java | 33 +++++++++++++ .../main/java/mage/counters/CounterType.java | 9 ++++ 4 files changed, 73 insertions(+), 30 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/LeechBonder.java b/Mage.Sets/src/mage/sets/shadowmoor/LeechBonder.java index 537910a30c..3877ea3184 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/LeechBonder.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/LeechBonder.java @@ -123,13 +123,16 @@ class LeechBonderEffect extends OneShotEffect { possibleChoices.add(counterName); } choice.setChoices(possibleChoices); - if (controller.choose(Outcome.AIDontUseIt, choice, game)) { + if (controller.choose(outcome, choice, game)) { String chosen = choice.getChoice(); if (fromPermanent.getCounters().containsKey(chosen)) { - Counter counter = new Counter(chosen, 1); - fromPermanent.removeCounters(counter, game); - toPermanent.addCounters(counter, game); - return true; + CounterType counterType = CounterType.findByName(chosen); + if (counterType != null) { + Counter counter = counterType.createInstance(); + fromPermanent.removeCounters(counter, game); + toPermanent.addCounters(counter, game); + return true; + } } } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProliferateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProliferateTest.java index da04e31658..c4ac4be761 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProliferateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ProliferateTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -38,15 +37,13 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - -public class ProliferateTest extends CardTestPlayerBase{ +public class ProliferateTest extends CardTestPlayerBase { /** - * Volt Charge {2}{R} - * Instant - * Volt Charge deals 3 damage to target creature or player. - * Proliferate. (You choose any number of permanents and/or players with counters - * on them, then give each another counter of a kind already there.) + * Volt Charge {2}{R} Instant Volt Charge deals 3 damage to target creature + * or player. Proliferate. (You choose any number of permanents and/or + * players with counters on them, then give each another counter of a kind + * already there.) */ @Test public void testCastFromHandMovedToExile() { @@ -55,11 +52,9 @@ public class ProliferateTest extends CardTestPlayerBase{ addCard(Zone.HAND, playerA, "Volt Charge"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volt Charge", playerB); addTarget(playerA, "Chandra, Pyromaster"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -69,19 +64,22 @@ public class ProliferateTest extends CardTestPlayerBase{ assertCounterCount("Chandra, Pyromaster", CounterType.LOYALTY, 5); // 4 + 1 from proliferate } - + /** - * Counters aren't cancelling each other out. Reproducible with any creature (graft and bloodthirst in my case) - * with a single +1/+1 counter on it, with a single -1/-1 placed on it (Grim Affliction, Instill Infection, etc). - * The counters should cancel each other out, leaving neither on the creature, which they don't (though visually - * there aren't any counters sitting on the card). Triggering proliferate at any point now (Thrumming Bird, - * Steady Progress, etc) will give you the option to add another of either counter, where you shouldn't have any as an option. + * Counters aren't cancelling each other out. Reproducible with any creature + * (graft and bloodthirst in my case) with a single +1/+1 counter on it, + * with a single -1/-1 placed on it (Grim Affliction, Instill Infection, + * etc). The counters should cancel each other out, leaving neither on the + * creature, which they don't (though visually there aren't any counters + * sitting on the card). Triggering proliferate at any point now (Thrumming + * Bird, Steady Progress, etc) will give you the option to add another of + * either counter, where you shouldn't have any as an option. */ @Test public void testValidTargets() { - addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // Put a +1/+1 counter on target creature. addCard(Zone.HAND, playerA, "Battlegrowth"); // {G} // Proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.) @@ -90,27 +88,27 @@ public class ProliferateTest extends CardTestPlayerBase{ addCard(Zone.BATTLEFIELD, playerB, "Sporeback Troll"); // has two +1/+1 counter addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); - // Put a -1/-1 counter on target creature, then proliferate. + // Put a -1/-1 counter on target creature, then proliferate. addCard(Zone.HAND, playerB, "Grim Affliction"); // {B}{2} castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Battlegrowth", "Silvercoat Lion"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grim Affliction", "Silvercoat Lion"); // proliferate Sporeback Troll - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Steady Progress"); // Silvercoat Lion may not be a valid target now - + setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); - assertGraveyardCount(playerA, "Battlegrowth", 1); assertGraveyardCount(playerA, "Steady Progress", 1); assertGraveyardCount(playerB, "Grim Affliction", 1); assertCounterCount("Silvercoat Lion", CounterType.P1P1, 0); // no valid target because no counter assertCounterCount("Sporeback Troll", CounterType.P1P1, 3); // 2 + 1 from proliferate + assertPowerToughness(playerB, "Sporeback Troll", 3, 3); - } + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counter/MovingCounterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counter/MovingCounterTest.java index 4b0d5b86fd..34528492d0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counter/MovingCounterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counter/MovingCounterTest.java @@ -106,4 +106,37 @@ public class MovingCounterTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Ruin Processor", 3, 4); } + + /** + * The card Leech Bonder (or the token mechanic) doesn't seem to work quite + * as intended. If moving a -1/-1 counter from the Leech Bonder onto an + * enemy creature with 1/1 the creature stays as a 1/1 with the token being + * displayed on it. Going by the rules the creature should have 0/0 and thus + * be put into the graveyard. + */ + @Test + public void testLeechBonder() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // Leech Bonder enters the battlefield with two -1/-1 counters on it. + // {U}, {untap}: Move a counter from target creature onto another target creature. + addCard(Zone.HAND, playerA, "Leech Bonder", 1);// Creature 3/3 - {2}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Ley Druid", 1); // 1/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Leech Bonder"); + + attack(3, playerA, "Leech Bonder"); + + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{U},", "Leech Bonder"); + addTarget(playerA, "Ley Druid"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 19); + + assertGraveyardCount(playerB, "Ley Druid", 1); + assertPowerToughness(playerA, "Leech Bonder", 2, 2); + + } } diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 5a1a359cff..4d41db4ee0 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -166,4 +166,13 @@ public enum CounterType { return new Counter(name, amount); } } + + public static CounterType findByName(String name) { + for (CounterType counterType : values()) { + if (counterType.getName().equals(name)) { + return counterType; + } + } + return null; + } } From 286dc55396f69953c93585678df64c4802097599 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Aug 2016 12:30:52 +0200 Subject: [PATCH 17/24] * Fixed Sightless Brawler cannot attack with other creatures attacking (fixes #2133). --- .../sets/journeyintonyx/SightlessBrawler.java | 5 +- .../cards/abilities/keywords/BestowTest.java | 28 +++++----- .../combat/CantAttackAloneAttachedEffect.java | 53 +++++++++++++++++++ ....java => CantAttackAloneSourceEffect.java} | 10 ++-- .../keyword/CantAttackAloneAbility.java | 4 +- 5 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java rename Mage/src/main/java/mage/abilities/effects/common/combat/{CantAttackAloneEffect.java => CantAttackAloneSourceEffect.java} (75%) diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/SightlessBrawler.java b/Mage.Sets/src/mage/sets/journeyintonyx/SightlessBrawler.java index f75112f483..e5487ea40a 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/SightlessBrawler.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/SightlessBrawler.java @@ -32,11 +32,12 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.combat.CanAttackOnlyAloneEffect; +import mage.abilities.effects.common.combat.CantAttackAloneAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.CantAttackAloneAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; @@ -65,7 +66,7 @@ public class SightlessBrawler extends CardImpl { Effect effect = new BoostEnchantedEffect(3, 2, Duration.WhileOnBattlefield); effect.setText("Enchanted creature gets +3/+2"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - effect = new CanAttackOnlyAloneEffect(); + effect = new CantAttackAloneAttachedEffect(AttachmentType.AURA); effect.setText("and can't attack alone"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java index 3674f10ff2..7d0cc9d104 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java @@ -362,7 +362,7 @@ public class BestowTest extends CardTestPlayerBase { Assert.assertEquals("Nighthowler has to be a creature", true, nighthowler.getCardType().contains(CardType.CREATURE)); } - + @Test public void testSightlessBrawlerCantAttackAloneEnforced() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); @@ -373,14 +373,14 @@ public class BestowTest extends CardTestPlayerBase { // Enchanted creature gets +3/+2 and can't attack alone. addCard(Zone.BATTLEFIELD, playerA, "Sightless Brawler"); - attack(1, playerA, "Sightless Brawler"); - setStopAt(1,PhaseStep.END_COMBAT); + attack(1, playerA, "Sightless Brawler"); + setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertLife(playerB, 20); assertTapped("Sightless Brawler", false); } - + @Test public void testSightlessBrawlerAttacksWithOthers() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); @@ -389,18 +389,18 @@ public class BestowTest extends CardTestPlayerBase { // Bestow 4W (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.) // Sightless Brawler can't attack alone. // Enchanted creature gets +3/+2 and can't attack alone. - addCard(Zone.BATTLEFIELD, playerA, "Sightless Brawler"); + addCard(Zone.BATTLEFIELD, playerA, "Sightless Brawler"); // 3/2 addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // {W} 2/1 creature attack(1, playerA, "Sightless Brawler"); attack(1, playerA, "Elite Vanguard"); - setStopAt(1,PhaseStep.END_COMBAT); + setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertLife(playerB, 15); assertTapped("Sightless Brawler", true); } - + @Test public void testSightlessBrawlerBestowedCantAttackAloneEnforced() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); @@ -414,15 +414,15 @@ public class BestowTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sightless Brawler using bestow", "Elite Vanguard"); attack(1, playerA, "Elite Vanguard"); - setStopAt(1,PhaseStep.END_COMBAT); + setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertHandCount(playerA, "Sightless Brawler", 0); assertLife(playerB, 20); assertTapped("Elite Vanguard", false); assertPowerToughness(playerA, "Elite Vanguard", 5, 3); // 2/1 + 3/2 = 5/3 } - + @Test public void testSightlessBrawlerBestowedAttacksWithOthers() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); @@ -438,9 +438,9 @@ public class BestowTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sightless Brawler using bestow", "Elite Vanguard"); attack(1, playerA, "Elite Vanguard"); attack(1, playerA, "Memnite"); - setStopAt(1,PhaseStep.END_COMBAT); + setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertHandCount(playerA, "Sightless Brawler", 0); assertLife(playerB, 14); assertTapped("Elite Vanguard", true); diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java new file mode 100644 index 0000000000..79a28e2703 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneAttachedEffect.java @@ -0,0 +1,53 @@ +/* + * 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. + */ +package mage.abilities.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.AttachmentType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantAttackAloneAttachedEffect extends RestrictionEffect { + + public CantAttackAloneAttachedEffect(AttachmentType attachmentType) { + super(Duration.WhileOnBattlefield); + if (attachmentType.equals(AttachmentType.AURA)) { + this.staticText = "Enchanted creature can't attack alone"; + } else { + this.staticText = "Equipped creature can't attack alone"; + } + } + + public CantAttackAloneAttachedEffect(final CantAttackAloneAttachedEffect effect) { + super(effect); + } + + @Override + public CantAttackAloneAttachedEffect copy() { + return new CantAttackAloneAttachedEffect(this); + } + + @Override + public boolean canAttackCheckAfter(int numberOfAttackers, Ability source, Game game) { + return numberOfAttackers > 1; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent attachment = game.getPermanent(source.getSourceId()); + if (attachment != null && attachment.getAttachedTo() != null + && permanent.getId().equals(attachment.getAttachedTo())) { + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java similarity index 75% rename from Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneEffect.java rename to Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java index 4c306d5fbc..5aa33b59e3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackAloneSourceEffect.java @@ -15,20 +15,20 @@ import mage.game.permanent.Permanent; * * @author LevelX2 */ -public class CantAttackAloneEffect extends RestrictionEffect { +public class CantAttackAloneSourceEffect extends RestrictionEffect { - public CantAttackAloneEffect() { + public CantAttackAloneSourceEffect() { super(Duration.WhileOnBattlefield); staticText = "{this} can't attack alone"; } - public CantAttackAloneEffect(final CantAttackAloneEffect effect) { + public CantAttackAloneSourceEffect(final CantAttackAloneSourceEffect effect) { super(effect); } @Override - public CantAttackAloneEffect copy() { - return new CantAttackAloneEffect(this); + public CantAttackAloneSourceEffect copy() { + return new CantAttackAloneSourceEffect(this); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/CantAttackAloneAbility.java b/Mage/src/main/java/mage/abilities/keyword/CantAttackAloneAbility.java index fecbd9dcdd..7ce38ab2a4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CantAttackAloneAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CantAttackAloneAbility.java @@ -28,7 +28,7 @@ package mage.abilities.keyword; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.combat.CantAttackAloneEffect; +import mage.abilities.effects.common.combat.CantAttackAloneSourceEffect; import mage.constants.Zone; /** @@ -37,7 +37,7 @@ import mage.constants.Zone; public class CantAttackAloneAbility extends SimpleStaticAbility { public CantAttackAloneAbility() { - super(Zone.BATTLEFIELD, new CantAttackAloneEffect()); + super(Zone.BATTLEFIELD, new CantAttackAloneSourceEffect()); } private CantAttackAloneAbility(CantAttackAloneAbility ability) { From 5b5c13c1376562fa4732a441fd40f6ef7151104b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Aug 2016 12:43:16 +0200 Subject: [PATCH 18/24] * Kusari-Gama - Fixed trigger occurs but no damage is dealt (fixes #2154). --- .../sets/championsofkamigawa/KusariGama.java | 14 ++++++-------- .../abilities/equipped/KusariGamaTest.java | 18 +++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KusariGama.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KusariGama.java index b8f9759df3..1000ec0d97 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KusariGama.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KusariGama.java @@ -64,7 +64,7 @@ public class KusariGama extends CardImpl { this.subtype.add("Equipment"); // Equipped creature has "{2}: This creature gets +1/+0 until end of turn." - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1,0, Duration.EndOfTurn), new GenericManaCost(2)); + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new GenericManaCost(2)); Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT); effect.setText("Equipped creature has \"{2}: This creature gets +1/+0 until end of turn.\""); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); @@ -144,22 +144,20 @@ class KusariGamaDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Integer damage = (Integer) this.getValue("damageAmount"); - if (damage != null && damage.intValue() > 0) { + if (damage != null && damage > 0) { UUID damagedCreatureId = (UUID) this.getValue("damagedCreatureId"); Permanent creature = game.getPermanent(damagedCreatureId); if (creature == null) { creature = (Permanent) game.getLastKnownInformation(damagedCreatureId, Zone.BATTLEFIELD); } if (creature != null) { - for (UUID blockerId : game.getCombat().getBlockers()) { - if (!blockerId.equals(damagedCreatureId)) { - Permanent blockingCreature = game.getPermanent(blockerId); - if (blockingCreature != null && blockingCreature.getControllerId().equals(creature.getControllerId())) { - blockingCreature.damage(damage, source.getSourceId(), game, false, true); - } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), creature.getControllerId(), game)) { + if (!permanent.getId().equals(damagedCreatureId)) { + permanent.damage(damage, source.getSourceId(), game, false, true); } } } + return true; } return false; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java index 5b9a376270..3fb7ff6dfb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/KusariGamaTest.java @@ -23,26 +23,26 @@ public class KusariGamaTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Kusari-Gama"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); addCard(Zone.BATTLEFIELD, playerA, "Sylvan Advocate"); // 2/3 vigilance {1}{G} - + addCard(Zone.BATTLEFIELD, playerB, "Wall of Omens"); // 0/4 {1}{W} addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); // 2/2 {1}{W} addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // 3/3 {3}{R} - - activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Equip {3}", "Sylvan Advocate"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Sylvan Advocate"); attack(1, playerA, "Sylvan Advocate"); block(1, playerB, "Wall of Omens", "Sylvan Advocate"); setStopAt(1, PhaseStep.END_COMBAT); execute(); - - assertPermanentCount(playerA, "Kusari-Gama", 1); + + assertPermanentCount(playerA, "Kusari-Gama", 1); assertPermanentCount(playerB, "Wall of Omens", 1); assertPermanentCount(playerB, "Hill Giant", 1); - + Permanent wallPerm = getPermanent("Wall of Omens", playerB); - Permanent giantPerm = getPermanent("Hill Giant", playerB); + Permanent giantPerm = getPermanent("Hill Giant", playerB); Assert.assertEquals("Wall of Omens should have 2 damage dealt to it", 2, wallPerm.getDamage()); - Assert.assertEquals("Hill Giant should have 2 damage dealt to it", 2, giantPerm.getDamage()); - + Assert.assertEquals("Hill Giant should have 2 damage dealt to it", 2, giantPerm.getDamage()); + assertGraveyardCount(playerB, "Silvercoat Lion", 2); } } From 7a4b358f8a580675c9f822618ec562121fce9a8c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Aug 2016 13:16:49 +0200 Subject: [PATCH 19/24] * Fixed CMC for spells (Escalate is not calculated correctly when more than 1 mode is chosen) fixes #2143. --- Mage/src/main/java/mage/abilities/SpellAbility.java | 10 ++-------- Mage/src/main/java/mage/game/stack/Spell.java | 8 +++----- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index a0c89bea84..e12d0c472d 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -176,8 +176,7 @@ public class SpellAbility extends ActivatedAbilityImpl { return cardName; } - public int getConvertedManaCost() { - int cmc = 0; + public int getConvertedXManaCost() { int xMultiplier = 0; for (String symbolString : getManaCosts().getSymbols()) { int index = symbolString.indexOf("{X}"); @@ -187,12 +186,7 @@ public class SpellAbility extends ActivatedAbilityImpl { index = symbolString.indexOf("{X}"); } } - if (getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { - cmc += getManaCostsToPay().getX() * xMultiplier; - } else { - cmc += getManaCosts().convertedManaCost() + getManaCostsToPay().getX() * xMultiplier; - } - return cmc; + return getManaCostsToPay().getX() * xMultiplier; } } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index e6252d91ed..83eeccda9c 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -365,7 +365,7 @@ public class Spell extends StackObjImpl implements Card { } } } else { - card.removeFromZone(game, Zone.STACK, sourceId); + card.removeFromZone(game, Zone.STACK, sourceId); } } @@ -503,11 +503,9 @@ public class Spell extends StackObjImpl implements Card { return 0; } for (SpellAbility spellAbility : spellAbilities) { - cmc += spellAbility.getConvertedManaCost(); - } - if (this.getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { - cmc += getCard().getManaCost().convertedManaCost(); + cmc += spellAbility.getConvertedXManaCost(); } + cmc += getCard().getManaCost().convertedManaCost(); return cmc; } From 73a30f5659e72ff79e2def3638f7f7b4e3954966 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Aug 2016 19:20:56 +0200 Subject: [PATCH 20/24] * Fixed wrong asserts of undying test (#2148) --- .../cards/abilities/keywords/UndyingTest.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 1d004f808a..5a2fa4d540 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -274,45 +274,46 @@ public class UndyingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Tatterkite", 3, 2); } - + /** - * I stole my opponents Vorapede using Simic Manipulator and shortly after someone played Wrath of God. - * Instead of returning to my opponent's board, Vorapede came back under my control. - * The rules text for Undying states that it should return under its owner's control, not its controller's. + * I stole my opponents Vorapede using Simic Manipulator and shortly after + * someone played Wrath of God. Instead of returning to my opponent's board, + * Vorapede came back under my control. The rules text for Undying states + * that it should return under its owner's control, not its controller's. */ @Test public void testUndyingCreatureReturnsUnderOwnersControl() { - + // Creature — Insect // Vigilance, trample // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) addCard(Zone.BATTLEFIELD, playerB, "Vorapede"); // {2}{G}{G}{G} 5/4 addCard(Zone.HAND, playerB, "Doom Blade"); // {1}{B} destroy target non-black creature addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - + // {2}{R} sorcery - Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. - addCard(Zone.HAND, playerA, "Act of Treason"); + addCard(Zone.HAND, playerA, "Act of Treason"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - + // playerA takes control of Vorapede from playerB castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Act of Treason", "Vorapede"); attack(1, playerA, "Vorapede"); // playerB kills Vorapede under the control of playerA now castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Doom Blade", "Vorapede"); - + setStopAt(1, PhaseStep.END_TURN); execute(); - + assertLife(playerA, 20); assertLife(playerB, 15); assertGraveyardCount(playerA, "Act of Treason", 1); assertGraveyardCount(playerB, "Doom Blade", 1); - + // Vorapede should return under control of playerA, not playerB - assertPermanentCount(playerA, "Vorapede", 1); - assertPermanentCount(playerB, "Vorapede", 0); - assertCounterCount(playerA, "Vorapede", CounterType.P1P1, 1); - assertPowerToughness(playerA, "Vorapede", 6, 5); + assertPermanentCount(playerB, "Vorapede", 1); + assertPermanentCount(playerA, "Vorapede", 0); + assertCounterCount(playerB, "Vorapede", CounterType.P1P1, 1); + assertPowerToughness(playerB, "Vorapede", 6, 5); } } From 32d70cb069f6761e13ea131a594f8fca5b6dc5ed Mon Sep 17 00:00:00 2001 From: Timothy Rogers Date: Sat, 6 Aug 2016 17:00:50 -0500 Subject: [PATCH 21/24] Changed Celestial Sword sacrifice trigger to use CreateDelayedTriggeredAbilityEffect and AtTheBeginOfNextEndStepDelayedTriggeredAbility --- Mage.Sets/src/mage/sets/iceage/CelestialSword.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/iceage/CelestialSword.java b/Mage.Sets/src/mage/sets/iceage/CelestialSword.java index 9acf2d7d88..cc80028aff 100644 --- a/Mage.Sets/src/mage/sets/iceage/CelestialSword.java +++ b/Mage.Sets/src/mage/sets/iceage/CelestialSword.java @@ -32,9 +32,12 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; @@ -59,7 +62,7 @@ public class CelestialSword extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(3, 3, Duration.EndOfTurn), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addEffect(new GainAbilityTargetEffect(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new SacrificeSourceEffect()),Duration.EndOfTurn)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeTargetEffect()))); this.addAbility(ability); } From 8ccb5d18eca1ad524ba5f4c158691a6cb3bcb99a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Aug 2016 09:28:39 +0200 Subject: [PATCH 22/24] * removing double negative checks. --- Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java | 5 ++--- Mage.Sets/src/mage/sets/urzassaga/LurkingEvil.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java index c9b1f66b85..5cc70e8b89 100644 --- a/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java +++ b/Mage.Sets/src/mage/sets/eighthedition/MurderousBetrayal.java @@ -28,7 +28,6 @@ package mage.sets.eighthedition; import java.util.UUID; - import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -95,7 +94,7 @@ class MurderousBetrayalCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && !(controller.getLife() > 0 && !controller.canPayLifeCost()); + return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost()); } @Override @@ -118,4 +117,4 @@ class MurderousBetrayalCost extends CostImpl { public MurderousBetrayalCost copy() { return new MurderousBetrayalCost(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/LurkingEvil.java b/Mage.Sets/src/mage/sets/urzassaga/LurkingEvil.java index b776eb275c..0799d78d41 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/LurkingEvil.java +++ b/Mage.Sets/src/mage/sets/urzassaga/LurkingEvil.java @@ -84,7 +84,7 @@ class LurkingEvilCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && !(controller.getLife() > 0 && !controller.canPayLifeCost()); + return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost()); } @Override From 07bbd53d8262dccd2e3d972c0117efd0931cbe46 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Aug 2016 10:12:38 +0200 Subject: [PATCH 23/24] * Stitcher's Graft - Fixed that the sacrifice effect did not work. --- .../sets/eldritchmoon/StitchersGraft.java | 4 +- .../test/cards/planeswalker/GideonTest.java | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/StitchersGraft.java b/Mage.Sets/src/mage/sets/eldritchmoon/StitchersGraft.java index 33f3f77342..9c5cea327e 100644 --- a/Mage.Sets/src/mage/sets/eldritchmoon/StitchersGraft.java +++ b/Mage.Sets/src/mage/sets/eldritchmoon/StitchersGraft.java @@ -34,7 +34,7 @@ import mage.abilities.common.UnattachedTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepSourceEffect; -import mage.abilities.effects.common.SacrificeEquippedEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; @@ -68,7 +68,7 @@ public class StitchersGraft extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // Whenever Stitcher's Graft becomes unattached from a permanent, sacrifice that permanent. - effect = new SacrificeEquippedEffect(); + effect = new SacrificeTargetEffect(); effect.setText("sacrifice that permanent"); this.addAbility(new UnattachedTriggeredAbility(effect, false)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java index ef3fe0da49..c139dae7fd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java @@ -31,6 +31,8 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -115,4 +117,47 @@ public class GideonTest extends CardTestPlayerBase { } + /** + * When you use Gideon, Battle-Forged (flipped version of Kytheon from Magic + * Origins) and use his 0 ability to turn him into a creature and equip the + * Stitcher's Graft from EMN, he should have to be sacced at the end of + * turn. + */ + @Test + public void testGideonBattleForgedSacrifice() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); + // At end of combat, if Kytheon, Hero of Akros and at least two other creatures attacked this combat, exile Kytheon, + // then return him to the battlefield transformed under his owner's control. + // {2}{W}: Kytheon gains indestructible until end of turn. + // Gideon, Battle-Forged + // +2: Up to one target creature an opponent controls attacks Gideon, Battle-Forged during its controller's next turn if able. + // +1: Until your next turn, target creature gains indestructible. Untap that creature. + // +0: Until end of turn, Gideon, Battle-Forged becomes a 4/4 Human Soldier creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn. + addCard(Zone.BATTLEFIELD, playerB, "Kytheon, Hero of Akros"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + // Equipped creature gets +3/+3. + // Whenever equipped creature attacks, it doesn't untap during its controller's next untap step. + // Whenever Stitcher's Graft becomes unattached from a permanent, sacrifice that permanent. + // Equip {2} + addCard(Zone.BATTLEFIELD, playerB, "Stitcher's Graft", 1); + + attack(2, playerB, "Kytheon, Hero of Akros"); + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Pillarfield Ox"); + + activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "+0: Until "); + activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Gideon, Battle-Forged"); + attack(4, playerB, "Gideon, Battle-Forged"); // 7 damage + + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertLife(playerA, 7); + Permanent equipment = getPermanent("Stitcher's Graft", playerB); + Assert.assertTrue("Stitcher's Graft may no longer be equipped", equipment.getAttachedTo() == null); + assertPermanentCount(playerB, "Gideon, Battle-Forged", 0); + assertGraveyardCount(playerB, "Kytheon, Hero of Akros", 1); + } } From 9e29a01c0c997867c17f8ff7e31e3080a5296a68 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Aug 2016 10:16:25 +0200 Subject: [PATCH 24/24] * Grafted Wargear - Fixed that the sacrifice effect did not work. --- Mage.Sets/src/mage/sets/fifthdawn/GraftedWargear.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/fifthdawn/GraftedWargear.java b/Mage.Sets/src/mage/sets/fifthdawn/GraftedWargear.java index d9aae29c4a..730a76ca52 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/GraftedWargear.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/GraftedWargear.java @@ -31,7 +31,7 @@ import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.UnattachedTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.SacrificeEquippedEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; @@ -55,7 +55,7 @@ public class GraftedWargear extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(3, 2))); // Whenever Grafted Wargear becomes unattached from a permanent, sacrifice that permanent. - this.addAbility(new UnattachedTriggeredAbility(new SacrificeEquippedEffect(), false)); + this.addAbility(new UnattachedTriggeredAbility(new SacrificeTargetEffect(), false)); // Equip {0} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(0)));