From 09a4267a46ccad004a95cb03d032ed90aa1be268 Mon Sep 17 00:00:00 2001 From: emerald000 Date: Sun, 24 May 2015 16:57:12 -0400 Subject: [PATCH 01/16] Added Boiling Seas, Cephalid Constable, Devastating Dreams and Life and Limb. --- .../mage/sets/judgment/CephalidConstable.java | 115 +++++++++++++ .../mage/sets/ninthedition/BoilingSeas.java | 65 +++++++ .../mage/sets/planarchaos/LifeAndLimb.java | 158 ++++++++++++++++++ .../src/mage/sets/portal/BoilingSeas.java | 52 ++++++ .../sets/tenthedition/CephalidConstable.java | 52 ++++++ .../mage/sets/torment/DevastatingDreams.java | 109 ++++++++++++ .../effects/common/SacrificeAllEffect.java | 4 +- 7 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/judgment/CephalidConstable.java create mode 100644 Mage.Sets/src/mage/sets/ninthedition/BoilingSeas.java create mode 100644 Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java create mode 100644 Mage.Sets/src/mage/sets/portal/BoilingSeas.java create mode 100644 Mage.Sets/src/mage/sets/tenthedition/CephalidConstable.java create mode 100644 Mage.Sets/src/mage/sets/torment/DevastatingDreams.java diff --git a/Mage.Sets/src/mage/sets/judgment/CephalidConstable.java b/Mage.Sets/src/mage/sets/judgment/CephalidConstable.java new file mode 100644 index 0000000000..0d3f8629c7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/judgment/CephalidConstable.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.judgment; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.target.Target; +import mage.target.TargetPermanent; + +/** + * + * @author emerald000 + */ +public class CephalidConstable extends CardImpl { + + public CephalidConstable(UUID ownerId) { + super(ownerId, 35, "Cephalid Constable", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + this.expansionSetCode = "JUD"; + this.subtype.add("Cephalid"); + this.subtype.add("Wizard"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever Cephalid Constable deals combat damage to a player, return up to that many target permanents that player controls to their owners' hands. + Ability ability = new CephalidConstableTriggeredAbility(); + ability.addTarget(new TargetPermanent(0, 1, new FilterPermanent(), false)); // Ajusted when it triggers. + this.addAbility(ability); + } + + public CephalidConstable(final CephalidConstable card) { + super(card); + } + + @Override + public CephalidConstable copy() { + return new CephalidConstable(this); + } +} + +class CephalidConstableTriggeredAbility extends TriggeredAbilityImpl { + + CephalidConstableTriggeredAbility() { + super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), false); + } + + CephalidConstableTriggeredAbility(final CephalidConstableTriggeredAbility ability) { + super(ability); + } + + @Override + public CephalidConstableTriggeredAbility copy() { + return new CephalidConstableTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getSourceId().equals(this.sourceId) && ((DamagedPlayerEvent) event).isCombatDamage()) { + FilterPermanent filter = new FilterPermanent("permanent" + (event.getAmount() > 1 ? "s" : "") + " damaged player control"); + filter.add(new ControllerIdPredicate(event.getPlayerId())); + Target target = new TargetPermanent(0, event.getAmount(), filter, false); + this.getTargets().clear(); + this.getTargets().add(target); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a player, return up to that many target permanents that player controls to their owners' hands"; + } + +} diff --git a/Mage.Sets/src/mage/sets/ninthedition/BoilingSeas.java b/Mage.Sets/src/mage/sets/ninthedition/BoilingSeas.java new file mode 100644 index 0000000000..c78108a055 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/BoilingSeas.java @@ -0,0 +1,65 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.ninthedition; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author emerald000 + */ +public class BoilingSeas extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Islands"); + static { + filter.add(new SubtypePredicate("Island")); + } + + public BoilingSeas(UUID ownerId) { + super(ownerId, 178, "Boiling Seas", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{R}"); + this.expansionSetCode = "9ED"; + + // Destroy all Islands. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + public BoilingSeas(final BoilingSeas card) { + super(card); + } + + @Override + public BoilingSeas copy() { + return new BoilingSeas(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java new file mode 100644 index 0000000000..ef75978269 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.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.planarchaos; + +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.AbilityAddingRemovingEffects_6; +import static mage.constants.Layer.PTChangingEffects_7; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class LifeAndLimb extends CardImpl { + + public LifeAndLimb(UUID ownerId) { + super(ownerId, 133, "Life and Limb", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + this.expansionSetCode = "PLC"; + + // All Forests and all Saprolings are 1/1 green Saproling creatures and Forest lands in addition to their other types. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LifeAndLimbEffect())); + } + + public LifeAndLimb(final LifeAndLimb card) { + super(card); + } + + @Override + public LifeAndLimb copy() { + return new LifeAndLimb(this); + } +} + +class LifeAndLimbEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterPermanent("All Forests and all Saprolings"); + static { + filter.add(Predicates.or(new SubtypePredicate("Forest"), new SubtypePredicate("Saproling"))); + } + + LifeAndLimbEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "All Forests and all Saprolings are 1/1 green Saproling creatures and Forest lands in addition to their other types"; + } + + LifeAndLimbEffect(final LifeAndLimbEffect effect) { + super(effect); + } + + @Override + public LifeAndLimbEffect copy() { + return new LifeAndLimbEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + if (!permanent.getCardType().contains(CardType.CREATURE)) { + permanent.getCardType().add(CardType.CREATURE); + } + if (!permanent.getSubtype().contains("Saproling")) { + permanent.getSubtype().add("Saproling"); + } + if (!permanent.getCardType().contains(CardType.LAND)) { + permanent.getCardType().add(CardType.LAND); + } + if (!permanent.getSubtype().contains("Forest")) { + permanent.getSubtype().add("Forest"); + } + break; + case ColorChangingEffects_5: + permanent.getColor().setColor(ObjectColor.GREEN); + break; + case AbilityAddingRemovingEffects_6: + boolean flag = false; + for (Ability ability : permanent.getAbilities(game)) { + if (ability instanceof GreenManaAbility) { + flag = true; + break; + } + } + if (!flag) { + permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + break; + case PTChangingEffects_7: + if (sublayer.equals(SubLayer.SetPT_7b)) { + permanent.getPower().setValue(1); + permanent.getToughness().setValue(1); + } + break; + } + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.PTChangingEffects_7; + } +} diff --git a/Mage.Sets/src/mage/sets/portal/BoilingSeas.java b/Mage.Sets/src/mage/sets/portal/BoilingSeas.java new file mode 100644 index 0000000000..a2c29bcea6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/portal/BoilingSeas.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.portal; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class BoilingSeas extends mage.sets.ninthedition.BoilingSeas { + + public BoilingSeas(UUID ownerId) { + super(ownerId); + this.cardNumber = 124; + this.expansionSetCode = "POR"; + } + + public BoilingSeas(final BoilingSeas card) { + super(card); + } + + @Override + public BoilingSeas copy() { + return new BoilingSeas(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tenthedition/CephalidConstable.java b/Mage.Sets/src/mage/sets/tenthedition/CephalidConstable.java new file mode 100644 index 0000000000..fbc5884eec --- /dev/null +++ b/Mage.Sets/src/mage/sets/tenthedition/CephalidConstable.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.tenthedition; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class CephalidConstable extends mage.sets.judgment.CephalidConstable { + + public CephalidConstable(UUID ownerId) { + super(ownerId); + this.cardNumber = 72; + this.expansionSetCode = "10E"; + } + + public CephalidConstable(final CephalidConstable card) { + super(card); + } + + @Override + public CephalidConstable copy() { + return new CephalidConstable(this); + } +} diff --git a/Mage.Sets/src/mage/sets/torment/DevastatingDreams.java b/Mage.Sets/src/mage/sets/torment/DevastatingDreams.java new file mode 100644 index 0000000000..55a583904f --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/DevastatingDreams.java @@ -0,0 +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.torment; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author emerald000 + */ +public class DevastatingDreams extends CardImpl { + + public DevastatingDreams(UUID ownerId) { + super(ownerId, 95, "Devastating Dreams", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{R}{R}"); + this.expansionSetCode = "TOR"; + + // As an additional cost to cast Devastating Dreams, discard X cards at random. + this.getSpellAbility().addCost(new DevastatingDreamsAdditionalCost()); + + // Each player sacrifices X lands. + this.getSpellAbility().addEffect(new SacrificeAllEffect(new GetXValue(), new FilterControlledLandPermanent("lands"))); + + // Devastating Dreams deals X damage to each creature. + this.getSpellAbility().addEffect(new DamageAllEffect(new GetXValue(), new FilterCreaturePermanent())); + } + + public DevastatingDreams(final DevastatingDreams card) { + super(card); + } + + @Override + public DevastatingDreams copy() { + return new DevastatingDreams(this); + } +} + +class DevastatingDreamsAdditionalCost extends VariableCostImpl { + + DevastatingDreamsAdditionalCost() { + super("cards to discard randomly"); + this.text = "As an additional cost to cast {this}, discard X cards at random"; + } + + DevastatingDreamsAdditionalCost(final DevastatingDreamsAdditionalCost cost) { + super(cost); + } + + @Override + public DevastatingDreamsAdditionalCost copy() { + return new DevastatingDreamsAdditionalCost(this); + } + + @Override + public int getMaxValue(Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + return controller.getHand().size(); + } + return 0; + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + TargetCardInHand target = new TargetCardInHand(xValue, new FilterCard()); + return new DiscardTargetCost(target, true); + } +} + diff --git a/Mage/src/mage/abilities/effects/common/SacrificeAllEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeAllEffect.java index 57a93b530d..12ca8a7485 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeAllEffect.java @@ -31,11 +31,11 @@ package mage.abilities.effects.common; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -110,7 +110,7 @@ public class SacrificeAllEffect extends OneShotEffect { private void setText() { StringBuilder sb = new StringBuilder(); - sb.append("Each players sacrifices "); + sb.append("Each player sacrifices "); if (amount.toString().equals("X")) { sb.append(amount.toString()); } else { From 194c2eb371a941376f5864b6b4e139d93365b30e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 00:57:42 +0200 Subject: [PATCH 02/16] * Reflecting Pool - Fixed that it did not work correctly with lands that produce conditional mana (e.g. Sliver Hive or Caverns of Soul) fixes #993. --- .../src/mage/sets/magic2015/SliverHive.java | 2 +- .../src/mage/sets/tempest/ReflectingPool.java | 7 ++++ .../test/cards/mana/ConditionalManaTest.java | 34 +++++++++++++++++-- .../mana/ConditionalAnyColorManaAbility.java | 6 ++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/SliverHive.java b/Mage.Sets/src/mage/sets/magic2015/SliverHive.java index 1d2c1a0578..d28e5e4d8e 100644 --- a/Mage.Sets/src/mage/sets/magic2015/SliverHive.java +++ b/Mage.Sets/src/mage/sets/magic2015/SliverHive.java @@ -70,7 +70,7 @@ public class SliverHive extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a Sliver spell. - this.addAbility(new ConditionalAnyColorManaAbility(1, new SliverHiveManaBuilder())); + this.addAbility(new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new SliverHiveManaBuilder(), true)); // {5}, {T}: Put a 1/1 colorless Sliver creature token onto the battlefield. Activate this ability only if you control a Sliver. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SliverToken()), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/sets/tempest/ReflectingPool.java b/Mage.Sets/src/mage/sets/tempest/ReflectingPool.java index c0c8f64313..005b0e53f8 100644 --- a/Mage.Sets/src/mage/sets/tempest/ReflectingPool.java +++ b/Mage.Sets/src/mage/sets/tempest/ReflectingPool.java @@ -179,6 +179,10 @@ class ReflectingPoolEffect extends ManaEffect { public List getNetMana(Game game, Ability source) { List netManas = new ArrayList<>(); Mana types = getManaTypes(game, source); + if (types.getAny()> 0) { + netManas.add(new Mana(0,0,0,0,0,0,1)); + return netManas; + } if (types.getBlack() > 0) { netManas.add(new Mana(ColoredManaSymbol.B)); } @@ -209,6 +213,9 @@ class ReflectingPoolEffect extends ManaEffect { if (!ability.equals(source) && ability.definesMana()) { for (Mana netMana: ability.getNetMana(game)) { types.add(netMana); + if (netMana.getAny() > 0) { + return types; + } } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java index 197b794d57..d7a9ca2029 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java @@ -29,7 +29,6 @@ package org.mage.test.cards.mana; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -73,5 +72,36 @@ public class ConditionalManaTest extends CardTestPlayerBase { assertHandCount(playerA, "Silvercoat Lion", 1); // player A could not cast Silvercoat Lion because the conditional mana is not available } - + + @Test + public void testWorkingWithReflectingPool() { + addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); + addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // can create white mana without restriction from the Cavern + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } + + @Test + public void testWorkingWithReflectingPool2() { + addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // can create white mana without restriction from the Hive + addCard(Zone.BATTLEFIELD, playerA, "Sliver Hive", 1); + addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {1} to your mana pool"); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add to your mana pool one mana of any type"); + setChoice(playerA, "White"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } } diff --git a/Mage/src/mage/abilities/mana/ConditionalAnyColorManaAbility.java b/Mage/src/mage/abilities/mana/ConditionalAnyColorManaAbility.java index 5aa3331e23..70190ef50a 100644 --- a/Mage/src/mage/abilities/mana/ConditionalAnyColorManaAbility.java +++ b/Mage/src/mage/abilities/mana/ConditionalAnyColorManaAbility.java @@ -77,6 +77,12 @@ public class ConditionalAnyColorManaAbility extends ManaAbility { return super.getNetMana(game); } + @Override + public boolean definesMana() { + return true; + } + + @Override public ConditionalAnyColorManaAbility copy() { return new ConditionalAnyColorManaAbility(this); From 0847d3f82090324ab8f1afe81dbd115eb97f1e47 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 10:51:44 +0200 Subject: [PATCH 03/16] * Fixed that player names for damage assigning during combat had html tags. --- Mage/src/mage/game/combat/CombatGroup.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage/src/mage/game/combat/CombatGroup.java b/Mage/src/mage/game/combat/CombatGroup.java index e5426bb6e2..3a07906c56 100644 --- a/Mage/src/mage/game/combat/CombatGroup.java +++ b/Mage/src/mage/game/combat/CombatGroup.java @@ -244,7 +244,7 @@ public class CombatGroup implements Serializable, Copyable { } else { Player player = game.getPlayer(attacker.getControllerId()); - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getLogName(), game); + int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); blocker.markDamage(damageAssigned, attacker.getId(), game, true, true); damage -= damageAssigned; if (damage > 0) { @@ -298,7 +298,7 @@ public class CombatGroup implements Serializable, Copyable { damage = 0; break; } - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getLogName(), game); + int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game); assigned.put(blockerId, damageAssigned); damage -= damageAssigned; } @@ -380,7 +380,7 @@ public class CombatGroup implements Serializable, Copyable { assigned.put(attackerId, damage); break; } - int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getLogName(), game); + int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getName(), game); assigned.put(attackerId, damageAssigned); damage -= damageAssigned; } From d3b41c076ddc87cda2f7597edbd60224bf7bf445 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 15:49:51 +0200 Subject: [PATCH 04/16] * Added DealsDamageAttachedTriggeredAbility and used in some cards. --- .../mage/sets/invasion/ArmadilloCloak.java | 90 ++----------------- .../mage/sets/seventhedition/SpiritLink.java | 84 +---------------- .../src/mage/sets/timespiral/SpiritLoop.java | 84 +---------------- .../NumericSetToEffectValueTest.java | 72 +++++++++++++++ .../DealsDamageAttachedTriggeredAbility.java | 62 +++++++++++++ ...sDamageGainLifeSourceTriggeredAbility.java | 1 - ...geToACreatureAttachedTriggeredAbility.java | 1 - .../common/NumericSetToEffectValues.java | 58 ++++++++++++ .../common/SignInversionDynamicValue.java | 2 +- .../effects/common/GainLifeEffect.java | 6 +- 10 files changed, 214 insertions(+), 246 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/NumericSetToEffectValueTest.java create mode 100644 Mage/src/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java create mode 100644 Mage/src/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java diff --git a/Mage.Sets/src/mage/sets/invasion/ArmadilloCloak.java b/Mage.Sets/src/mage/sets/invasion/ArmadilloCloak.java index c517f2bb14..87596dd3e2 100644 --- a/Mage.Sets/src/mage/sets/invasion/ArmadilloCloak.java +++ b/Mage.Sets/src/mage/sets/invasion/ArmadilloCloak.java @@ -29,11 +29,12 @@ package mage.sets.invasion; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsDamageAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; @@ -45,10 +46,6 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -63,7 +60,6 @@ public class ArmadilloCloak extends CardImpl { this.expansionSetCode = "INV"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -72,11 +68,14 @@ public class ArmadilloCloak extends CardImpl { this.addAbility(ability); // Enchanted creature gets +2/+2 and has trample. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA))); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA); + effect.setText("and has trample"); + ability.addEffect(effect); + this.addAbility(ability); // Whenever enchanted creature deals damage, you gain that much life. - this.addAbility(new ArmadilloCloakTriggeredAbility()); + this.addAbility(new DealsDamageAttachedTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); } @@ -89,74 +88,3 @@ public class ArmadilloCloak extends CardImpl { return new ArmadilloCloak(this); } } - -class ArmadilloCloakTriggeredAbility extends TriggeredAbilityImpl { - - public ArmadilloCloakTriggeredAbility() { - super(Zone.BATTLEFIELD, new ArmadilloCloakEffect(), false); - } - - public ArmadilloCloakTriggeredAbility(final ArmadilloCloakTriggeredAbility ability) { - super(ability); - } - - @Override - public ArmadilloCloakTriggeredAbility copy() { - return new ArmadilloCloakTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { - Permanent enchantment = game.getPermanent(this.getSourceId()); - if (enchantment == null || enchantment.getAttachedTo() == null) { - return false; - } - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null && event.getSourceId().equals(enchanted.getId())) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature deals damage, " + super.getRule(); - } -} - -class ArmadilloCloakEffect extends OneShotEffect { - - public ArmadilloCloakEffect() { - super(Outcome.GainLife); - this.staticText = "you gain that much life"; - } - - public ArmadilloCloakEffect(final ArmadilloCloakEffect effect) { - super(effect); - } - - @Override - public ArmadilloCloakEffect copy() { - return new ArmadilloCloakEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.gainLife(amount, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/seventhedition/SpiritLink.java b/Mage.Sets/src/mage/sets/seventhedition/SpiritLink.java index 9845f487f6..f973d97ec8 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/SpiritLink.java +++ b/Mage.Sets/src/mage/sets/seventhedition/SpiritLink.java @@ -29,20 +29,16 @@ package mage.sets.seventhedition; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DealsDamageAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -57,7 +53,6 @@ public class SpiritLink extends CardImpl { this.expansionSetCode = "7ED"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -66,7 +61,7 @@ public class SpiritLink extends CardImpl { this.addAbility(ability); // Whenever enchanted creature deals damage, you gain that much life. - this.addAbility(new SpiritLinkTriggeredAbility()); + this.addAbility(new DealsDamageAttachedTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); } public SpiritLink(final SpiritLink card) { @@ -78,74 +73,3 @@ public class SpiritLink extends CardImpl { return new SpiritLink(this); } } - -class SpiritLinkTriggeredAbility extends TriggeredAbilityImpl { - - public SpiritLinkTriggeredAbility() { - super(Zone.BATTLEFIELD, new SpiritLinkEffect(), false); - } - - public SpiritLinkTriggeredAbility(final SpiritLinkTriggeredAbility ability) { - super(ability); - } - - @Override - public SpiritLinkTriggeredAbility copy() { - return new SpiritLinkTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { - Permanent enchantment = game.getPermanent(this.getSourceId()); - if (enchantment == null || enchantment.getAttachedTo() == null) { - return false; - } - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null && event.getSourceId().equals(enchanted.getId())) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature deals damage, " + super.getRule(); - } -} - -class SpiritLinkEffect extends OneShotEffect { - - public SpiritLinkEffect() { - super(Outcome.GainLife); - this.staticText = "you gain that much life"; - } - - public SpiritLinkEffect(final SpiritLinkEffect effect) { - super(effect); - } - - @Override - public SpiritLinkEffect copy() { - return new SpiritLinkEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.gainLife(amount, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/timespiral/SpiritLoop.java b/Mage.Sets/src/mage/sets/timespiral/SpiritLoop.java index 3760226eb7..3c8a325682 100644 --- a/Mage.Sets/src/mage/sets/timespiral/SpiritLoop.java +++ b/Mage.Sets/src/mage/sets/timespiral/SpiritLoop.java @@ -32,19 +32,15 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DealsDamageAttachedTriggeredAbility; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.EnchantAbility; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -58,7 +54,6 @@ public class SpiritLoop extends CardImpl { this.expansionSetCode = "TSP"; this.subtype.add("Aura"); - // Enchant creature you control TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -67,7 +62,7 @@ public class SpiritLoop extends CardImpl { this.addAbility(ability); // Whenever enchanted creature deals damage, you gain that much life. - this.addAbility(new SpiritLoopTriggeredAbility()); + this.addAbility(new DealsDamageAttachedTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); // When Spirit Loop is put into a graveyard from the battlefield, return Spirit Loop to its owner's hand. this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new ReturnToHandSourceEffect())); @@ -82,75 +77,4 @@ public class SpiritLoop extends CardImpl { public SpiritLoop copy() { return new SpiritLoop(this); } -} - -class SpiritLoopTriggeredAbility extends TriggeredAbilityImpl { - - public SpiritLoopTriggeredAbility() { - super(Zone.BATTLEFIELD, new SpiritLoopEffect(), false); - } - - public SpiritLoopTriggeredAbility(final SpiritLoopTriggeredAbility ability) { - super(ability); - } - - @Override - public SpiritLoopTriggeredAbility copy() { - return new SpiritLoopTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { - Permanent enchantment = game.getPermanent(this.getSourceId()); - if (enchantment == null || enchantment.getAttachedTo() == null) { - return false; - } - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null && event.getSourceId().equals(enchanted.getId())) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature deals damage, " + super.getRule(); - } -} - -class SpiritLoopEffect extends OneShotEffect { - - public SpiritLoopEffect() { - super(Outcome.GainLife); - this.staticText = "you gain that much life"; - } - - public SpiritLoopEffect(final SpiritLoopEffect effect) { - super(effect); - } - - @Override - public SpiritLoopEffect copy() { - return new SpiritLoopEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.gainLife(amount, game); - return true; - } - } - return false; - } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/NumericSetToEffectValueTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/NumericSetToEffectValueTest.java new file mode 100644 index 0000000000..e4656134b2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/NumericSetToEffectValueTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.dynamicvalue; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class NumericSetToEffectValueTest extends CardTestPlayerBase { + + + /** + * Check that the dealt damage is added to life + * + */ + @Test + public void ArmadilloCloakTest() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + // Enchant creature + // Enchanted creature gets +2/+2 and has trample. + // Whenever enchanted creature deals damage, you gain that much life. + addCard(Zone.HAND, playerA, "Armadillo Cloak"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armadillo Cloak", "Silvercoat Lion"); + + attack(3, playerA, "Silvercoat Lion"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA,"Armadillo Cloak", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 4, 4); + + assertLife(playerA, 24); + assertLife(playerB, 16); + + } + +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java new file mode 100644 index 0000000000..dd7e55a5aa --- /dev/null +++ b/Mage/src/mage/abilities/common/DealsDamageAttachedTriggeredAbility.java @@ -0,0 +1,62 @@ +/* + * 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.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ + +public class DealsDamageAttachedTriggeredAbility extends TriggeredAbilityImpl { + + public DealsDamageAttachedTriggeredAbility(Zone zone, Effect effect, boolean optional) { + super(zone, effect, optional); + } + + public DealsDamageAttachedTriggeredAbility(final DealsDamageAttachedTriggeredAbility ability) { + super(ability); + } + + @Override + public DealsDamageAttachedTriggeredAbility copy() { + return new DealsDamageAttachedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) + || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent enchantment = game.getPermanent(this.getSourceId()); + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + if (enchanted != null && event.getSourceId().equals(enchanted.getId())) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever enchanted creature deals damage, " + super.getRule(); + } +} diff --git a/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java b/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java index 5da95952af..026276a1f7 100644 --- a/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java @@ -28,7 +28,6 @@ package mage.abilities.common; -import static javax.xml.bind.JAXBIntrospector.getValue; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; diff --git a/Mage/src/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java index 9405da194e..816cee3d4f 100644 --- a/Mage/src/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java @@ -34,7 +34,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.DamagedCreatureEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; diff --git a/Mage/src/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java b/Mage/src/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java new file mode 100644 index 0000000000..b9ab0d6d72 --- /dev/null +++ b/Mage/src/mage/abilities/dynamicvalue/common/NumericSetToEffectValues.java @@ -0,0 +1,58 @@ +/* + * 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.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ + +public class NumericSetToEffectValues implements DynamicValue { + + private final String message; + private final String valueKey; + + + public NumericSetToEffectValues(String message, String valueKey) { + this.message = message; + this.valueKey = valueKey; + } + + public NumericSetToEffectValues(final NumericSetToEffectValues dynamicValue) { + super(); + this.message = dynamicValue.message; + this.valueKey = dynamicValue.valueKey; + } + + @Override + public int calculate(Game game, Ability source, Effect effect) { + Object object = effect.getValue(valueKey); + if (object instanceof Integer) { + return (Integer) object; + } + return 0; + } + + @Override + public NumericSetToEffectValues copy() { + return new NumericSetToEffectValues(this); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/Mage/src/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java b/Mage/src/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java index 3e927496ed..2b50242ec4 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java @@ -6,7 +6,7 @@ import mage.abilities.effects.Effect; import mage.game.Game; public class SignInversionDynamicValue implements DynamicValue { - private DynamicValue value; + private final DynamicValue value; public SignInversionDynamicValue(DynamicValue value) { this.value = value.copy(); diff --git a/Mage/src/mage/abilities/effects/common/GainLifeEffect.java b/Mage/src/mage/abilities/effects/common/GainLifeEffect.java index 064edd652a..c01b6923cf 100644 --- a/Mage/src/mage/abilities/effects/common/GainLifeEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainLifeEffect.java @@ -86,11 +86,13 @@ public class GainLifeEffect extends OneShotEffect { StringBuilder sb = new StringBuilder(); String message = life.getMessage(); sb.append("you gain "); - if (message.isEmpty() || !message.equals("1")) { + if (message.startsWith("that")) { + sb.append(message).append(" "); + } else if (message.isEmpty() || !message.equals("1")) { sb.append(life).append(" "); } sb.append("life"); - if (message.length() > 0) { + if (message.length() > 0 && !message.startsWith("that")) { sb.append(message.equals("1") ? " equal to the number of " : " for each "); sb.append(message); } From b753654384321f06e5de5932141585763c306393 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 15:50:50 +0200 Subject: [PATCH 05/16] * Added Arcbound Crasher, Mana Clash, Tel-Jilad Stylus, Chance Encounter and Vampiric Link. --- .../mage/sets/darksteel/ArcboundCrusher.java | 52 +++++++++ .../mage/sets/eighthedition/ManaClash.java | 52 +++++++++ .../src/mage/sets/fifthedition/ManaClash.java | 52 +++++++++ .../mage/sets/fourthedition/ManaClash.java | 108 ++++++++++++++++++ .../src/mage/sets/mirrodin/KrarksThumb.java | 17 ++- .../mage/sets/mirrodin/TelJiladStylus.java | 67 +++++++++++ .../src/mage/sets/ninthedition/ManaClash.java | 52 +++++++++ .../mage/sets/odyssey/ChanceEncounter.java | 105 +++++++++++++++++ .../mage/sets/planarchaos/VampiricLink.java | 75 ++++++++++++ .../mage/sets/planechase/ArcboundCrusher.java | 82 +++++++++++++ .../sets/scarsofmirrodin/GlintHawkIdol.java | 1 + .../mage/sets/seventhedition/ManaClash.java | 52 +++++++++ .../src/mage/sets/thedark/ManaClash.java | 52 +++++++++ .../common/PutOnLibraryTargetEffect.java | 34 +++--- Mage/src/mage/game/events/GameEvent.java | 2 +- Mage/src/mage/players/PlayerImpl.java | 8 +- 16 files changed, 784 insertions(+), 27 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/darksteel/ArcboundCrusher.java create mode 100644 Mage.Sets/src/mage/sets/eighthedition/ManaClash.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/ManaClash.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/ManaClash.java create mode 100644 Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java create mode 100644 Mage.Sets/src/mage/sets/ninthedition/ManaClash.java create mode 100644 Mage.Sets/src/mage/sets/odyssey/ChanceEncounter.java create mode 100644 Mage.Sets/src/mage/sets/planarchaos/VampiricLink.java create mode 100644 Mage.Sets/src/mage/sets/planechase/ArcboundCrusher.java create mode 100644 Mage.Sets/src/mage/sets/seventhedition/ManaClash.java create mode 100644 Mage.Sets/src/mage/sets/thedark/ManaClash.java diff --git a/Mage.Sets/src/mage/sets/darksteel/ArcboundCrusher.java b/Mage.Sets/src/mage/sets/darksteel/ArcboundCrusher.java new file mode 100644 index 0000000000..83f267ab0c --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/ArcboundCrusher.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.darksteel; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class ArcboundCrusher extends mage.sets.planechase.ArcboundCrusher { + + public ArcboundCrusher(UUID ownerId) { + super(ownerId); + this.cardNumber = 95; + this.expansionSetCode = "DST"; + } + + public ArcboundCrusher(final ArcboundCrusher card) { + super(card); + } + + @Override + public ArcboundCrusher copy() { + return new ArcboundCrusher(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eighthedition/ManaClash.java b/Mage.Sets/src/mage/sets/eighthedition/ManaClash.java new file mode 100644 index 0000000000..232905b94f --- /dev/null +++ b/Mage.Sets/src/mage/sets/eighthedition/ManaClash.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.eighthedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class ManaClash extends mage.sets.fourthedition.ManaClash { + + public ManaClash(UUID ownerId) { + super(ownerId); + this.cardNumber = 202; + this.expansionSetCode = "8ED"; + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/ManaClash.java b/Mage.Sets/src/mage/sets/fifthedition/ManaClash.java new file mode 100644 index 0000000000..3dac56f688 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/ManaClash.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 LevelX2 + */ +public class ManaClash extends mage.sets.fourthedition.ManaClash { + + public ManaClash(UUID ownerId) { + super(ownerId); + this.cardNumber = 248; + this.expansionSetCode = "5ED"; + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/ManaClash.java b/Mage.Sets/src/mage/sets/fourthedition/ManaClash.java new file mode 100644 index 0000000000..3af5080683 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/ManaClash.java @@ -0,0 +1,108 @@ +/* + * 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.fourthedition; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author LevelX2 + */ +public class ManaClash extends CardImpl { + + public ManaClash(UUID ownerId) { + super(ownerId, 228, "Mana Clash", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{R}"); + this.expansionSetCode = "4ED"; + + // You and target opponent each flip a coin. Mana Clash deals 1 damage to each player whose coin comes up tails. Repeat this process until both players' coins come up heads on the same flip. + this.getSpellAbility().addEffect(new ManaClashEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} + +class ManaClashEffect extends OneShotEffect { + + public ManaClashEffect() { + super(Outcome.Detriment); + this.staticText = "You and target opponent each flip a coin. {this} deals 1 damage to each player whose coin comes up tails. Repeat this process until both players' coins come up heads on the same flip"; + } + + public ManaClashEffect(final ManaClashEffect effect) { + super(effect); + } + + @Override + public ManaClashEffect copy() { + return new ManaClashEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller != null && targetOpponent != null) { + boolean bothHeads = false; + while (!bothHeads) { + if (!targetOpponent.isInGame() || !controller.isInGame()) { + return false; + } + boolean controllerFlip = controller.flipCoin(game); + boolean opponentFlip = targetOpponent.flipCoin(game); + if (controllerFlip && opponentFlip) { + bothHeads = true; + } + if (!controllerFlip) { + controller.damage(1, source.getSourceId(), game, false, true); + } + if (!opponentFlip) { + targetOpponent.damage(1, source.getSourceId(), game, false, true); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/KrarksThumb.java b/Mage.Sets/src/mage/sets/mirrodin/KrarksThumb.java index efd82bfd8e..fda4d43fcc 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/KrarksThumb.java +++ b/Mage.Sets/src/mage/sets/mirrodin/KrarksThumb.java @@ -27,6 +27,7 @@ */ package mage.sets.mirrodin; +import java.util.Random; import java.util.UUID; import mage.constants.CardType; import mage.constants.Duration; @@ -80,7 +81,11 @@ class KrarksThumbEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { - boolean secondCoinFlip = player.flipCoin(game, event.getAppliedEffects()); + // because second flip is ignored it may not be done by the player method + boolean secondCoinFlip = new Random().nextBoolean(); + if (!game.isSimulation()) { + game.informPlayers("[Flip a coin] " + player.getLogName() + (secondCoinFlip ? " won (head)." : " lost (tail).")); + } if (player.chooseUse(outcome, "Ignore the first coin flip?", game)) { event.setFlag(secondCoinFlip); game.informPlayers(new StringBuilder(player.getLogName()).append(" ignores the first coin flip.").toString()); @@ -91,12 +96,14 @@ class KrarksThumbEffect extends ReplacementEffectImpl { return false; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.FLIP_COIN; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.FLIP_COIN && source.getControllerId().equals(event.getPlayerId())) { - return true; - } - return false; + return source.getControllerId().equals(event.getPlayerId()); } @Override diff --git a/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java b/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java new file mode 100644 index 0000000000..56f529fa89 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.mirrodin; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author LevelX2 + */ +public class TelJiladStylus extends CardImpl { + + public TelJiladStylus(UUID ownerId) { + super(ownerId, 260, "Tel-Jilad Stylus", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); + this.expansionSetCode = "MRD"; + + // {T}: Put target permanent you own on the bottom of your library. + Effect effect = new PutOnLibraryTargetEffect(false,"put target permanent you own on the bottom of your library"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + ability.addTarget(new TargetControlledPermanent()); + this.addAbility(ability); + } + + public TelJiladStylus(final TelJiladStylus card) { + super(card); + } + + @Override + public TelJiladStylus copy() { + return new TelJiladStylus(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ninthedition/ManaClash.java b/Mage.Sets/src/mage/sets/ninthedition/ManaClash.java new file mode 100644 index 0000000000..cfc678f1fd --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/ManaClash.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.ninthedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class ManaClash extends mage.sets.fourthedition.ManaClash { + + public ManaClash(UUID ownerId) { + super(ownerId); + this.cardNumber = 203; + this.expansionSetCode = "9ED"; + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} diff --git a/Mage.Sets/src/mage/sets/odyssey/ChanceEncounter.java b/Mage.Sets/src/mage/sets/odyssey/ChanceEncounter.java new file mode 100644 index 0000000000..d93d7c6b4b --- /dev/null +++ b/Mage.Sets/src/mage/sets/odyssey/ChanceEncounter.java @@ -0,0 +1,105 @@ +/* + * 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.odyssey; + +import java.util.UUID; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LevelX2 + */ +public class ChanceEncounter extends CardImpl { + + public ChanceEncounter(UUID ownerId) { + super(ownerId, 182, "Chance Encounter", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}"); + this.expansionSetCode = "ODY"; + + // Whenever you win a coin flip, put a luck counter on Chance Encounter. + this.addAbility(new ChanceEncounterTriggeredAbility()); + + // At the beginning of your upkeep, if Chance Encounter has ten or more luck counters on it, you win the game. + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new WinGameSourceControllerEffect(), TargetController.YOU, false); + this.addAbility(new ConditionalTriggeredAbility(ability, new SourceHasCounterCondition(CounterType.LUCK, 10, Integer.MAX_VALUE), + "At the beginning of your upkeep, if {this} has ten or more luck counters on it, you win the game")); + } + + public ChanceEncounter(final ChanceEncounter card) { + super(card); + } + + @Override + public ChanceEncounter copy() { + return new ChanceEncounter(this); + } +} + +class ChanceEncounterTriggeredAbility extends TriggeredAbilityImpl { + + public ChanceEncounterTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.LUCK.createInstance()), false); + } + + public ChanceEncounterTriggeredAbility(final ChanceEncounterTriggeredAbility ability) { + super(ability); + } + + @Override + public ChanceEncounterTriggeredAbility copy() { + return new ChanceEncounterTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COIN_FLIPPED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return this.getControllerId().equals(event.getPlayerId()) && event.getFlag(); + } + + @Override + public String getRule() { + return "Whenever you win a coin flip, " + super.getRule(); + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/VampiricLink.java b/Mage.Sets/src/mage/sets/planarchaos/VampiricLink.java new file mode 100644 index 0000000000..1cdd62a59f --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/VampiricLink.java @@ -0,0 +1,75 @@ +/* + * 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.planarchaos; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class VampiricLink extends CardImpl { + + public VampiricLink(UUID ownerId) { + super(ownerId, 92, "Vampiric Link", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + this.expansionSetCode = "PLC"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature deals damage, you gain that much life. + this.addAbility(new DealsDamageAttachedTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); + } + + public VampiricLink(final VampiricLink card) { + super(card); + } + + @Override + public VampiricLink copy() { + return new VampiricLink(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planechase/ArcboundCrusher.java b/Mage.Sets/src/mage/sets/planechase/ArcboundCrusher.java new file mode 100644 index 0000000000..c616edcfcf --- /dev/null +++ b/Mage.Sets/src/mage/sets/planechase/ArcboundCrusher.java @@ -0,0 +1,82 @@ +/* + * 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.planechase; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.ModularAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.permanent.AnotherPredicate; + +/** + * + * @author LevelX2 + */ +public class ArcboundCrusher extends CardImpl { + + private static final FilterPermanent filter = new FilterArtifactPermanent("another artifact"); + + static { + filter.add(new AnotherPredicate()); + } + + public ArcboundCrusher(UUID ownerId) { + super(ownerId, 105, "Arcbound Crusher", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "HOP"; + this.subtype.add("Juggernaut"); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever another artifact enters the battlefield, put a +1/+1 counter on Arcbound Crusher. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter)); + + // Modular 1 + this.addAbility(new ModularAbility(this, 1)); + } + + public ArcboundCrusher(final ArcboundCrusher card) { + super(card); + } + + @Override + public ArcboundCrusher copy() { + return new ArcboundCrusher(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GlintHawkIdol.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GlintHawkIdol.java index 6c912c4529..036480edf6 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GlintHawkIdol.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GlintHawkIdol.java @@ -69,6 +69,7 @@ public class GlintHawkIdol extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GlintHawkIdolToken(), "", Duration.EndOfTurn), filter, true)); + // {W}: Glint Hawk Idol becomes a 2/2 Bird artifact creature with flying until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GlintHawkIdolToken(), "", Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.W))); } diff --git a/Mage.Sets/src/mage/sets/seventhedition/ManaClash.java b/Mage.Sets/src/mage/sets/seventhedition/ManaClash.java new file mode 100644 index 0000000000..05e2cb5897 --- /dev/null +++ b/Mage.Sets/src/mage/sets/seventhedition/ManaClash.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.seventhedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class ManaClash extends mage.sets.fourthedition.ManaClash { + + public ManaClash(UUID ownerId) { + super(ownerId); + this.cardNumber = 202; + this.expansionSetCode = "7ED"; + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/ManaClash.java b/Mage.Sets/src/mage/sets/thedark/ManaClash.java new file mode 100644 index 0000000000..16e9e6dd95 --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/ManaClash.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.thedark; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class ManaClash extends mage.sets.fourthedition.ManaClash { + + public ManaClash(UUID ownerId) { + super(ownerId); + this.cardNumber = 71; + this.expansionSetCode = "DRK"; + } + + public ManaClash(final ManaClash card) { + super(card); + } + + @Override + public ManaClash copy() { + return new ManaClash(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/PutOnLibraryTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutOnLibraryTargetEffect.java index 82e673820d..a337cfc3d0 100644 --- a/Mage/src/mage/abilities/effects/common/PutOnLibraryTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutOnLibraryTargetEffect.java @@ -144,25 +144,25 @@ public class PutOnLibraryTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { - StringBuilder sb = new StringBuilder(); if (this.staticText != null && !this.staticText.isEmpty()) { - sb.append(staticText); - } else { - Target target = mode.getTargets().get(0); - sb.append("Put "); - if (target.getMaxNumberOfTargets() == 0) { - sb.append("any number of "); - } else { - if (target.getMaxNumberOfTargets() != 1 || target.getNumberOfTargets() != 1) { - if (target.getMaxNumberOfTargets() > target.getNumberOfTargets()) { - sb.append("up to "); - } - sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" "); - } - } - sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" on "); - sb.append(onTop ? "top" : "the bottom").append(" of it's owner's library"); + return staticText; } + StringBuilder sb = new StringBuilder(); + Target target = mode.getTargets().get(0); + sb.append("Put "); + if (target.getMaxNumberOfTargets() == 0) { + sb.append("any number of "); + } else { + if (target.getMaxNumberOfTargets() != 1 || target.getNumberOfTargets() != 1) { + if (target.getMaxNumberOfTargets() > target.getNumberOfTargets()) { + sb.append("up to "); + } + sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" "); + } + } + sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" on "); + sb.append(onTop ? "top" : "the bottom").append(" of it's owner's library"); + return sb.toString(); } diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 50e4ab491f..aae2b81a16 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -175,7 +175,7 @@ public class GameEvent implements Serializable { SHUFFLE_LIBRARY, LIBRARY_SHUFFLED, ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, - FLIP_COIN, SCRY, FATESEAL, + FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL, //permanent events ENTERS_THE_BATTLEFIELD, diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 20a8e6cd8a..e964a2522b 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1665,9 +1665,7 @@ public abstract class PlayerImpl implements Player, Serializable { } else if (source instanceof CommandObject){ sourceControllerId = ((CommandObject) source).getControllerId(); sourceAbilities = ((CommandObject) source).getAbilities(); - } else { - source = null; - } + } } else { sourceAbilities = ((Permanent) source).getAbilities(game); sourceControllerId = ((Permanent) source).getControllerId(); @@ -2110,7 +2108,9 @@ public abstract class PlayerImpl implements Player, Serializable { } GameEvent event = new GameEvent(GameEvent.EventType.FLIP_COIN, playerId, null, playerId, 0, result); event.setAppliedEffects(appliedEffects); - game.replaceEvent(event); + if (!game.replaceEvent(event)) { + game.fireEvent(new GameEvent(GameEvent.EventType.COIN_FLIPPED, playerId, null, playerId, 0, event.getFlag())); + } return event.getFlag(); } From 4298e66e028b8231da39296bdd7cd5450386b450 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 16:51:09 +0200 Subject: [PATCH 06/16] * Changeling - Fixed that some non creature subtypes of cards (e.g. Arcane) were positive filtered for Changeling (fixes #991). --- .../LongForgottenGohei.java | 4 +- .../abilities/keywords/ChangelingTest.java | 63 +++++++++++++++++++ Mage/src/mage/MageObjectImpl.java | 12 ++-- Mage/src/mage/util/CardUtil.java | 12 +++- 4 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/LongForgottenGohei.java b/Mage.Sets/src/mage/sets/championsofkamigawa/LongForgottenGohei.java index 2cac959bbe..35429ee0b0 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/LongForgottenGohei.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/LongForgottenGohei.java @@ -45,7 +45,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; /** * - * @author Ludwig + * @author LevelX2 */ public class LongForgottenGohei extends CardImpl { @@ -61,8 +61,10 @@ public class LongForgottenGohei extends CardImpl { public LongForgottenGohei(UUID ownerId) { super(ownerId, 261, "Long-Forgotten Gohei", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); this.expansionSetCode = "CHK"; + // Arcane spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(arcaneFilter, 1))); + // Spirit creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, spiritFilter, false))); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java new file mode 100644 index 0000000000..c201054815 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ChangelingTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ChangelingTest extends CardTestPlayerBase { + + /** + * Casting changelings with a Long-Forgotten Gohei in play reduces its casting cost by {1}. + */ + + @Test + public void testLongForgottenGohei() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.HAND, playerA, "Woodland Changeling"); + + addCard(Zone.BATTLEFIELD, playerA, "Long-Forgotten Gohei"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Woodland Changeling"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Woodland Changeling", 0); // Casting cost of spell is not reduced so not on the battlefield + assertHandCount(playerA, "Woodland Changeling", 1); + + } + +} \ No newline at end of file diff --git a/Mage/src/mage/MageObjectImpl.java b/Mage/src/mage/MageObjectImpl.java index ea8ec87718..83eb715b6f 100644 --- a/Mage/src/mage/MageObjectImpl.java +++ b/Mage/src/mage/MageObjectImpl.java @@ -40,6 +40,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.ChangelingAbility; import mage.constants.CardType; import mage.game.Game; +import mage.util.CardUtil; import mage.util.GameLog; public abstract class MageObjectImpl implements MageObject { @@ -176,17 +177,14 @@ public abstract class MageObjectImpl implements MageObject { } if (this.subtype.contains(value)) { return true; - } - else { // checking for Changeling - // first make sure input parameter is not creature type + } else { // checking for Changeling + // first make sure input parameter is a creature type // if so, then ChangelingAbility doesn't matter - if (value.equals("Mountain") || value.equals("Island") || value.equals("Plains") - || value.equals("Forest") || value.equals("Swamp") || value.equals("Aura") - || value.equals("Equipment") || value.equals("Fortification") || value.equals("Shrine")) { + if (CardUtil.isNonCreatureSubtype(value)) { return false; } // as it is creature subtype, then check the existence of Changeling - return abilities.contains(ChangelingAbility.getInstance()) || this.subtype.contains(ChangelingAbility.ALL_CREATURE_TYPE); + return abilities.contains(ChangelingAbility.getInstance()) || this.subtype.contains(ChangelingAbility.ALL_CREATURE_TYPE); } } diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index 32705d881b..475c3a2c10 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -28,6 +28,7 @@ package mage.util; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -74,6 +75,12 @@ public class CardUtil { static String numberStrings[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "ninteen", "twenty"}; + public static final String[] NON_CHANGELING_SUBTYPES_VALUES = new String[] { "Mountain", "Forest", "Plains", "Swamp", "Island", + "Aura", "Curse", "Shrine", + "Equipment", "Fortification", "Contraption", + "Trap", "Arcane"}; + public static final Set NON_CREATURE_SUBTYPES = new HashSet<>(Arrays.asList(NON_CHANGELING_SUBTYPES_VALUES)); + /** * Checks whether two cards share card types. * @@ -637,5 +644,8 @@ public class CardUtil { } return mana; } - + + public static boolean isNonCreatureSubtype(String subtype) { + return NON_CREATURE_SUBTYPES.contains(subtype); + } } From fe2d41944ab1c1ad9e863166c37f2be94f85cf97 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 16:57:11 +0200 Subject: [PATCH 07/16] * Dawn Charm - Fixed that the prevention effect did prevent all damage not only combat damage. --- Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java b/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java index f15dcfc3b6..fe3b795c17 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java +++ b/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java @@ -62,14 +62,14 @@ public class DawnCharm extends CardImpl { this.expansionSetCode = "PLC"; - // Choose one - Prevent all combat damage that would be dealt this turn; or regenerate target creature; or counter target spell that targets you. - this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn)); - + // Choose one - Prevent all combat damage that would be dealt this turn + this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn), true); + // or regenerate target creature; Mode mode = new Mode(); mode.getEffects().add(new RegenerateTargetEffect()); mode.getTargets().add(new TargetCreaturePermanent()); this.getSpellAbility().addMode(mode); - + // or counter target spell that targets you. mode = new Mode(); mode.getEffects().add(new CounterTargetEffect()); mode.getTargets().add(new TargetSpell(filter)); From 4dea3635923cfd324947e1e3fd20fa30c5aed32f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 16:58:28 +0200 Subject: [PATCH 08/16] * Dawn Charm - Fixed that the prevention effect did prevent all damage not only combat damage. --- Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java b/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java index fe3b795c17..0a1917f461 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java +++ b/Mage.Sets/src/mage/sets/planarchaos/DawnCharm.java @@ -63,7 +63,7 @@ public class DawnCharm extends CardImpl { // Choose one - Prevent all combat damage that would be dealt this turn - this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn), true); + this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(Duration.EndOfTurn, true)); // or regenerate target creature; Mode mode = new Mode(); mode.getEffects().add(new RegenerateTargetEffect()); From 58a8e7dc7560597d1abb0e31a58d4ee6eaa4237a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 17:13:39 +0200 Subject: [PATCH 09/16] Added missing implementation of Flicker and Flickering Spirit. --- .../mage/sets/timespiral/FlickeringSpirit.java | 11 +++++++++++ .../src/mage/sets/urzasdestiny/Flicker.java | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java b/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java index 240355f9bd..93e1dbde31 100644 --- a/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java +++ b/Mage.Sets/src/mage/sets/timespiral/FlickeringSpirit.java @@ -29,10 +29,16 @@ package mage.sets.timespiral; import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.Zone; /** * @@ -49,7 +55,12 @@ public class FlickeringSpirit extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // {3}{W}: Exile Flickering Spirit, then return it to the battlefield under its owner's control. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(true), new ManaCostsImpl("{3}{W}")); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlSourceEffect()); + this.addAbility(ability); + } public FlickeringSpirit(final FlickeringSpirit card) { diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java b/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java index b690ecf5f9..d60bacd520 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Flicker.java @@ -28,21 +28,37 @@ package mage.sets.urzasdestiny; import java.util.UUID; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.target.TargetPermanent; /** * * @author LevelX2 */ public class Flicker extends CardImpl { - + + private static final FilterPermanent filter = new FilterPermanent("nontoken permanent"); + + static { + filter.add(Predicates.not(new TokenPredicate())); + } + public Flicker(UUID ownerId) { super(ownerId, 9, "Flicker", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{W}"); this.expansionSetCode = "UDS"; // Exile target nontoken permanent, then return it to the battlefield under its owner's control. + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + } public Flicker(final Flicker card) { From 77def0a112e2e6439a2f535c42823ce80628a3a8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 17:18:12 +0200 Subject: [PATCH 10/16] * Mindcrank - Fixed that the triggered effect did not work. --- Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java b/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java index 56411b9a64..cecf7dd574 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/Mindcrank.java @@ -54,6 +54,8 @@ public class Mindcrank extends CardImpl { super(ownerId, 144, "Mindcrank", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "NPH"; + // Whenever an opponent loses life, that player puts that many cards from the top of his or her library into his or her graveyard. + // (Damage dealt by sources without infect causes loss of life.) this.addAbility(new MindcrankTriggeredAbility()); } @@ -91,8 +93,10 @@ class MindcrankTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Set opponents = game.getOpponents(this.getControllerId()); if (opponents.contains(event.getPlayerId())) { - Effect effect = this.getEffects().get(0); - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + for (Effect effect : this.getEffects()) { + effect.setValue("lostLife", event.getAmount()); + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + } return true; } return false; @@ -123,7 +127,7 @@ class MindcrankEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); if (targetPlayer != null) { - Integer amount = (Integer) getValue("amount"); + Integer amount = (Integer) getValue("lostLife"); if (amount == null) { amount = 0; } From c71b3ea7e80e3f1cc2a0528f47a96c1d4fcead1e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 18:39:36 +0200 Subject: [PATCH 11/16] * Proliferate - Fixed that in some cases permanents that had counters before could be choosen tp proliferate. --- .../abilities/keywords/ProliferateTest.java | 44 +++++++++++++++++++ Mage/src/mage/counters/Counters.java | 8 +++- 2 files changed, 50 insertions(+), 2 deletions(-) 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 d8d9751f26..316543911b 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 @@ -69,4 +69,48 @@ 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. + */ + @Test + public void testValidTargets() { + 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.) + // Draw a card. + addCard(Zone.HAND, playerA, "Steady Progress"); // {U}{2} + + 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. + 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 + + } } diff --git a/Mage/src/mage/counters/Counters.java b/Mage/src/mage/counters/Counters.java index d842c33541..fd304bea44 100644 --- a/Mage/src/mage/counters/Counters.java +++ b/Mage/src/mage/counters/Counters.java @@ -57,7 +57,7 @@ public class Counters extends HashMap implements Serializable { this.put(name, new Counter(name)); } this.get(name).add(); - } + } public void addCounter(String name, int amount) { if (!this.containsKey(name)) { @@ -76,7 +76,11 @@ public class Counters extends HashMap implements Serializable { public void removeCounter(String name) { if (this.containsKey(name)) { - this.get(name).remove(); + Counter counter = this.get(name); + counter.remove(); + if (counter.getCount() == 0) { + this.remove(name); + } } } From 7ca6c87fe5b57348d05bacb3eb66ab3953df5205 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 19:05:00 +0200 Subject: [PATCH 12/16] * Unearth - Fixed the not working replacement effect of unearth that exiles the creature if neccessary. --- .../mage/sets/conflux/HellsparkElemental.java | 2 +- .../cards/abilities/keywords/UnearthTest.java | 78 +++++++++++++++++++ .../abilities/keyword/UnearthAbility.java | 13 +++- 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UnearthTest.java diff --git a/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java b/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java index b4d33ca052..cb274cb430 100644 --- a/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java +++ b/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java @@ -63,7 +63,7 @@ public class HellsparkElemental extends CardImpl { // At the beginning of the end step, sacrifice Hellspark Elemental. this.addAbility(new OnEventTriggeredAbility(EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new SacrificeSourceEffect())); - // Unearth {1}{R} ({1}{R}: Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.) + // Unearth {1}{R}: Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.) this.addAbility(new UnearthAbility(new ManaCostsImpl("{1}{R}"))); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UnearthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UnearthTest.java new file mode 100644 index 0000000000..1d6911fd8e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UnearthTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class UnearthTest extends CardTestPlayerBase { + + /** + * Hellspark Elemental (and probably other cards with the unearth ability) - If I unearth the elemental, + * attack, and then go to the end of my turn both the "sacrifice" and "exile" clauses will trigger and + * the game will ask me which one I want to put on the stack first. If I choose "sacrifice" first and + * "exile" second, all good, the exile part resolves first and the elemental is exiled, the sacrifice + * part does nothing afterwards. But if I choose "exile" first and "sacrifice" second then the elemental + * will be sacrificed and placed on my graveyard and after that the "exile" resolves but does nothing, as + * I'm guessing it can't "find" the elemental anymore and so it stays in my graveyard, despite the fact + * that because I use its unearth ability it should always be exiled once leaving the battlefield no matter what. + * The bug should be easy to reproduce if following the order I mention above (click the exile part, + * so the sacrifice goes on the top of the stack). + */ + @Test + public void testUnearthAttackExile() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // 3/1 - Trample, haste + // At the beginning of the end step, sacrifice Hellspark Elemental. + // Unearth {1}{R} ({1}{R}: Return this card from your graveyard to the battlefield. + // It gains haste. Exile it at the beginning of the next end step or if it would + // leave the battlefield. Unearth only as a sorcery.) + addCard(Zone.GRAVEYARD, playerA, "Hellspark Elemental", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unearth"); + + attack(1, playerA, "Hellspark Elemental"); + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + assertGraveyardCount(playerA, "Hellspark Elemental", 0); + assertLife(playerB, 17); + + assertPermanentCount(playerA, "Hellspark Elemental", 0); + assertExileCount("Hellspark Elemental", 1); + } + +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/UnearthAbility.java b/Mage/src/mage/abilities/keyword/UnearthAbility.java index 19e9b4724b..c8411cfb87 100644 --- a/Mage/src/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/mage/abilities/keyword/UnearthAbility.java @@ -45,6 +45,8 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; /** * @@ -144,10 +146,11 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (source.getSourceObjectIfItStillExists(game) != null) { + if (event.getTargetId().equals(source.getSourceId())) { ZoneChangeEvent zEvent = (ZoneChangeEvent)event; if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) { - return true; + // started in graveyard goint to battlefield so current zone change counter has to be +1 + return source.getSourceObjectZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId()); } } return false; @@ -155,7 +158,11 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - new ExileSourceEffect().apply(game, source); + Permanent permanent = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && permanent != null) { + controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true); + } return true; } } From b71ae8aeef10b7f2fe0afcbbf9761e470633b000 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 19:08:46 +0200 Subject: [PATCH 13/16] * Commune with the Gods - Fixed that the choosen card went wrongly to graveyard instead to hand as intended. --- Mage.Sets/src/mage/sets/theros/CommuneWithTheGods.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/theros/CommuneWithTheGods.java b/Mage.Sets/src/mage/sets/theros/CommuneWithTheGods.java index 1a16b21924..e38d8d7c34 100644 --- a/Mage.Sets/src/mage/sets/theros/CommuneWithTheGods.java +++ b/Mage.Sets/src/mage/sets/theros/CommuneWithTheGods.java @@ -56,7 +56,6 @@ public class CommuneWithTheGods extends CardImpl { super(ownerId, 155, "Commune with the Gods", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{G}"); this.expansionSetCode = "THS"; - // Reveal the top five cards of your library. You may put a creature or enchantment card from among them into your hand. Put the rest into your graveyard. this.getSpellAbility().addEffect(new CommuneWithTheGodsEffect()); @@ -119,7 +118,7 @@ class CommuneWithTheGodsEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { cards.remove(card); - controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); } } From d0ed98466fcee052b23c344a5b48de702dab59f6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 19:24:21 +0200 Subject: [PATCH 14/16] * Mana Tapping - Fixed a bug that prevented to undo mana tapping done without paying for a spell or ability. --- Mage/src/mage/abilities/mana/ManaAbility.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/mage/abilities/mana/ManaAbility.java b/Mage/src/mage/abilities/mana/ManaAbility.java index 7155dd8ea5..afb90a01d2 100644 --- a/Mage/src/mage/abilities/mana/ManaAbility.java +++ b/Mage/src/mage/abilities/mana/ManaAbility.java @@ -63,6 +63,7 @@ public abstract class ManaAbility extends ActivatedAbilityImpl { public ManaAbility(final ManaAbility ability) { super(ability); this.netMana.addAll(ability.netMana); + this.undoPossible = ability.undoPossible; } @Override From 382cee762977bc11c2460a714bd2e55ccce917f7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 19:29:18 +0200 Subject: [PATCH 15/16] Fixed target of Tel-Jilad Stylus. --- .../src/mage/sets/mirrodin/TelJiladStylus.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java b/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java index 56f529fa89..b6a5e99a57 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java +++ b/Mage.Sets/src/mage/sets/mirrodin/TelJiladStylus.java @@ -36,8 +36,11 @@ import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.target.common.TargetControlledPermanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.other.OwnerPredicate; +import mage.target.TargetPermanent; /** * @@ -45,6 +48,12 @@ import mage.target.common.TargetControlledPermanent; */ public class TelJiladStylus extends CardImpl { + private final static FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(new OwnerPredicate(TargetController.YOU)); + } + public TelJiladStylus(UUID ownerId) { super(ownerId, 260, "Tel-Jilad Stylus", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); this.expansionSetCode = "MRD"; @@ -52,7 +61,7 @@ public class TelJiladStylus extends CardImpl { // {T}: Put target permanent you own on the bottom of your library. Effect effect = new PutOnLibraryTargetEffect(false,"put target permanent you own on the bottom of your library"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - ability.addTarget(new TargetControlledPermanent()); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } From 9f935a42db3922662c2229425db9fe0947e08d4b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2015 19:32:11 +0200 Subject: [PATCH 16/16] * Early Harvest - Fixed that it wrongly only untaps non basic lands instead of basic lands. --- Mage.Sets/src/mage/sets/ninthedition/EarlyHarvest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/ninthedition/EarlyHarvest.java b/Mage.Sets/src/mage/sets/ninthedition/EarlyHarvest.java index 546cc205f7..73b1e44f7f 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/EarlyHarvest.java +++ b/Mage.Sets/src/mage/sets/ninthedition/EarlyHarvest.java @@ -71,7 +71,7 @@ class UntapAllLandsTargetEffect extends OneShotEffect { private static final FilterLandPermanent filter = new FilterLandPermanent(); static { - filter.add(Predicates.not(new SupertypePredicate("Basic"))); + filter.add(new SupertypePredicate("Basic")); } public UntapAllLandsTargetEffect() {