From ece8248f11edbee2eeac8b5309287dd30ac83f96 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 25 Jul 2015 11:34:04 +0300 Subject: [PATCH 01/18] Implement cards: Destructive Flow, Radiant Kavu, and Warped Devotion --- .../sets/eighthedition/WarpedDevotion.java | 107 ++++++++++++++++++ .../mage/sets/planeshift/DestructiveFlow.java | 71 ++++++++++++ .../src/mage/sets/planeshift/RadiantKavu.java | 76 +++++++++++++ .../mage/sets/planeshift/WarpedDevotion.java | 54 +++++++++ 4 files changed, 308 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java create mode 100644 Mage.Sets/src/mage/sets/planeshift/DestructiveFlow.java create mode 100644 Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java create mode 100644 Mage.Sets/src/mage/sets/planeshift/WarpedDevotion.java diff --git a/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java b/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java new file mode 100644 index 00000000000..62f78c48d0f --- /dev/null +++ b/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java @@ -0,0 +1,107 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.eighthedition; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LoneFox + */ +public class WarpedDevotion extends CardImpl { + + public WarpedDevotion(UUID ownerId) { + super(ownerId, 172, "Warped Devotion", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + this.expansionSetCode = "8ED"; + + // Whenever a permanent is returned to a player's hand, that player discards a card. + this.addAbility(new WarpedDevotionTriggeredAbility()); + } + + public WarpedDevotion(final WarpedDevotion card) { + super(card); + } + + @Override + public WarpedDevotion copy() { + return new WarpedDevotion(this); + } +} + +class WarpedDevotionTriggeredAbility extends TriggeredAbilityImpl { + + public WarpedDevotionTriggeredAbility() { + super(Zone.BATTLEFIELD, new DiscardTargetEffect(1), false); + } + + public WarpedDevotionTriggeredAbility(final WarpedDevotionTriggeredAbility ability) { + super(ability); + } + + public WarpedDevotionTriggeredAbility copy() { + return new WarpedDevotionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zce = (ZoneChangeEvent)event; + if(zce.getFromZone() == Zone.BATTLEFIELD && zce.getToZone() == Zone.HAND) { + Card card = game.getCard(event.getTargetId()); + if(card != null) { + for(Effect effect: getEffects()) { + effect.setTargetPointer(new FixedTarget(card.getOwnerId())); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever a permanent is returned to a player's hand, that player discards a card"; + } + +} diff --git a/Mage.Sets/src/mage/sets/planeshift/DestructiveFlow.java b/Mage.Sets/src/mage/sets/planeshift/DestructiveFlow.java new file mode 100644 index 00000000000..79dff24deeb --- /dev/null +++ b/Mage.Sets/src/mage/sets/planeshift/DestructiveFlow.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.planeshift; + +import java.util.UUID; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.SacrificeEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; + +/** + * + * @author LoneFox + */ +public class DestructiveFlow extends CardImpl { + + private static final FilterLandPermanent filter = new FilterLandPermanent("nonbasic land"); + + static { + filter.add(Predicates.not(new SupertypePredicate("Basic"))); + } + + public DestructiveFlow(UUID ownerId) { + super(ownerId, 102, "Destructive Flow", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{B}{R}{G}"); + this.expansionSetCode = "PLS"; + + // At the beginning of each player's upkeep, that player sacrifices a nonbasic land. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeEffect(filter, 1, "that player"), + TargetController.ANY, false)); + + } + + public DestructiveFlow(final DestructiveFlow card) { + super(card); + } + + @Override + public DestructiveFlow copy() { + return new DestructiveFlow(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java b/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java new file mode 100644 index 00000000000..b460f739148 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java @@ -0,0 +1,76 @@ +/* + * 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.planeshift; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventAllDamageByAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author LoneFox + */ +public class RadiantKavu extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue and black creatures"); + static { + filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLUE), new ColorPredicate(ObjectColor.BLACK))); + } + + public RadiantKavu(UUID ownerId) { + super(ownerId, 120, "Radiant Kavu", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{R}{G}{W}"); + this.expansionSetCode = "PLS"; + this.subtype.add("Kavu"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {R}{G}{W}: Prevent all combat damage blue creatures and black creatures would deal this turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, (new PreventAllDamageByAllEffect(filter, + Duration.EndOfTurn, true)), new ManaCostsImpl("{R}{G}{W}"))); + } + + public RadiantKavu(final RadiantKavu card) { + super(card); + } + + @Override + public RadiantKavu copy() { + return new RadiantKavu(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planeshift/WarpedDevotion.java b/Mage.Sets/src/mage/sets/planeshift/WarpedDevotion.java new file mode 100644 index 00000000000..776a40c4dce --- /dev/null +++ b/Mage.Sets/src/mage/sets/planeshift/WarpedDevotion.java @@ -0,0 +1,54 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.planeshift; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class WarpedDevotion extends mage.sets.eighthedition.WarpedDevotion { + + public WarpedDevotion(UUID ownerId) { + super(ownerId); + this.cardNumber = 57; + this.expansionSetCode = "PLS"; + this.rarity = Rarity.UNCOMMON; + } + + public WarpedDevotion(final WarpedDevotion card) { + super(card); + } + + @Override + public WarpedDevotion copy() { + return new WarpedDevotion(this); + } +} From 3ea281ffbf5fd16acaeab34e54c68437719f297c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 25 Jul 2015 20:12:43 +0300 Subject: [PATCH 02/18] Add DamageTargetControllerEffect and use it for existing cards. Implement card: Illuminate --- .../src/mage/sets/apocalypse/Illuminate.java | 79 ++++++++++++++ .../sets/betrayersofkamigawa/FirstVolley.java | 50 ++------- .../mage/sets/magic2011/ChandrasOutrage.java | 61 +++-------- .../mage/sets/magicorigins/RavagingBlaze.java | 56 ++-------- .../common/DamageTargetControllerEffect.java | 103 ++++++++++++++++++ 5 files changed, 215 insertions(+), 134 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/Illuminate.java create mode 100644 Mage/src/mage/abilities/effects/common/DamageTargetControllerEffect.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/Illuminate.java b/Mage.Sets/src/mage/sets/apocalypse/Illuminate.java new file mode 100644 index 00000000000..d5e8769fbb2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/Illuminate.java @@ -0,0 +1,79 @@ +/* + * 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.apocalypse; + +import java.util.UUID; +import mage.abilities.condition.common.KickedCostCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class Illuminate extends CardImpl { + + public Illuminate(UUID ownerId) { + super(ownerId, 63, "Illuminate", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{X}{R}"); + this.expansionSetCode = "APC"; + + // Kicker {2}{R} and/or {3}{U} + KickerAbility kickerAbility = new KickerAbility("{2}{R}"); + kickerAbility.addKickerCost("{3}{U}"); + this.addAbility(kickerAbility); + // Illuminate deals X damage to target creature. If Illuminate was kicked with its {2}{R} kicker, it deals X damage to that creature's controller. If Illuminate was kicked with its {3}{U} kicker, you draw X cards. + this.getSpellAbility().addEffect(new DamageTargetEffect(new ManacostVariableValue())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DamageTargetControllerEffect(new ManacostVariableValue()), + new KickedCostCondition("{2}{R}"), + "If {this} was kicked with its {2}{R} kicker, it deals X damage to that creature's controller.")); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(new ManacostVariableValue()), + new KickedCostCondition("{3}{U}"), + "If {this} was kicked with its {3}{U} kicker, you draw X cards.")); + + } + + public Illuminate(final Illuminate card) { + super(card); + } + + @Override + public Illuminate copy() { + return new Illuminate(this); + } +} diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/FirstVolley.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/FirstVolley.java index 8f20e822b03..58ad9dd54f7 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/FirstVolley.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/FirstVolley.java @@ -28,15 +28,12 @@ package mage.sets.betrayersofkamigawa; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.target.common.TargetCreaturePermanent; /** @@ -50,9 +47,11 @@ public class FirstVolley extends CardImpl { this.expansionSetCode = "BOK"; this.subtype.add("Arcane"); - // First Volley deals 1 damage to target creature and 1 damage to that creature's controller. - this.getSpellAbility().addEffect(new FirstVolleyEffect()); + this.getSpellAbility().addEffect(new DamageTargetEffect(1)); + Effect effect = new DamageTargetControllerEffect(1); + effect.setText("and 1 damage to that creature's controller"); + this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -66,34 +65,3 @@ public class FirstVolley extends CardImpl { return new FirstVolley(this); } } - -class FirstVolleyEffect extends OneShotEffect { - - public FirstVolleyEffect() { - super(Outcome.Damage); - this.staticText = "{this} deals 1 damage to target creature and 1 damage to that creature's controller"; - } - - public FirstVolleyEffect(final FirstVolleyEffect effect) { - super(effect); - } - - @Override - public FirstVolleyEffect copy() { - return new FirstVolleyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null) { - Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null) { - permanent.damage(1, source.getSourceId(), game, false, true); - controller.damage(1, source.getSourceId(), game, false, true); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/magic2011/ChandrasOutrage.java b/Mage.Sets/src/mage/sets/magic2011/ChandrasOutrage.java index a5ee07e9ce7..98d75760c25 100644 --- a/Mage.Sets/src/mage/sets/magic2011/ChandrasOutrage.java +++ b/Mage.Sets/src/mage/sets/magic2011/ChandrasOutrage.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -29,15 +29,12 @@ package mage.sets.magic2011; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.target.common.TargetCreaturePermanent; /** @@ -50,7 +47,11 @@ public class ChandrasOutrage extends CardImpl { super(ownerId, 128, "Chandra's Outrage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}{R}"); this.expansionSetCode = "M11"; - this.getSpellAbility().addEffect(new ChandrasOutrageEffect()); + // Chandra's Outrage deals 4 damage to target creature and 2 damage to that creature's controller. + this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + Effect effect = new DamageTargetControllerEffect(2); + effect.setText("and 2 damage to that creature's controller"); + this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -64,35 +65,3 @@ public class ChandrasOutrage extends CardImpl { } } - -class ChandrasOutrageEffect extends OneShotEffect { - - public ChandrasOutrageEffect() { - super(Outcome.Damage); - staticText = "{this} deals 4 damage to target creature and 2 damage to that creature's controller"; - } - - public ChandrasOutrageEffect(final ChandrasOutrageEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - permanent.damage(4, source.getSourceId(), game, false, true); - player.damage(2, source.getSourceId(), game, false, true); - return true; - } - } - return false; - } - - @Override - public ChandrasOutrageEffect copy() { - return new ChandrasOutrageEffect(this); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magicorigins/RavagingBlaze.java b/Mage.Sets/src/mage/sets/magicorigins/RavagingBlaze.java index 15d9672c16f..a22d8aa8feb 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/RavagingBlaze.java +++ b/Mage.Sets/src/mage/sets/magicorigins/RavagingBlaze.java @@ -28,16 +28,14 @@ package mage.sets.magicorigins; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.condition.common.SpellMasteryCondition; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; /** @@ -50,10 +48,12 @@ public class RavagingBlaze extends CardImpl { super(ownerId, 159, "Ravaging Blaze", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{X}{R}{R}"); this.expansionSetCode = "ORI"; - // Ravaging Blaze deals X damage to target creature. - // Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Ravaging Blaze also deals X damage to that creature's controller. - this.getSpellAbility().addEffect(new RavagingBlazeEffect()); + // Ravaging Blaze deals X damage to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(new ManacostVariableValue())); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + // Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Ravaging Blaze also deals X damage to that creature's controller. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageTargetControllerEffect(new ManacostVariableValue()), + SpellMasteryCondition.getInstance(), "
Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, Ravaging Blaze also deals X damage to that creature's controller.")); } public RavagingBlaze(final RavagingBlaze card) { @@ -65,41 +65,3 @@ public class RavagingBlaze extends CardImpl { return new RavagingBlaze(this); } } - -class RavagingBlazeEffect extends OneShotEffect { - - public RavagingBlazeEffect() { - super(Outcome.Damage); - staticText = "{this} deals X damage to target creature.
" - + "Spell mastery — If there are two or more instant and/or sorcery cards in your graveyard, {this} also deals X damage to that creature's controller."; - } - - public RavagingBlazeEffect(final RavagingBlazeEffect effect) { - super(effect); - } - - @Override - public RavagingBlazeEffect copy() { - return new RavagingBlazeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - int xValue = source.getManaCostsToPay().getX(); - if (xValue > 0) { - permanent.damage(xValue, source.getSourceId(), game, false, true); - if (SpellMasteryCondition.getInstance().apply(game, source)) { - Player targetController = game.getPlayer(permanent.getControllerId()); - if (targetController != null) { - targetController.damage(xValue, source.getSourceId(), game, false, true); - } - } - } - return true; - } - return false; - } - -} diff --git a/Mage/src/mage/abilities/effects/common/DamageTargetControllerEffect.java b/Mage/src/mage/abilities/effects/common/DamageTargetControllerEffect.java new file mode 100644 index 00000000000..d8d7216746f --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/DamageTargetControllerEffect.java @@ -0,0 +1,103 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author LoneFox + */ +public class DamageTargetControllerEffect extends OneShotEffect { + + protected DynamicValue amount; + protected boolean preventable; + + public DamageTargetControllerEffect(int amount) { + this(new StaticValue(amount), true); + } + + public DamageTargetControllerEffect(int amount, boolean preventable) { + this(new StaticValue(amount), preventable); + } + + public DamageTargetControllerEffect(DynamicValue amount) { + this(amount, true); + } + + public DamageTargetControllerEffect(DynamicValue amount, boolean preventable) { + super(Outcome.Damage); + this.amount = amount; + this.preventable = preventable; + } + + public DamageTargetControllerEffect(final DamageTargetControllerEffect effect) { + super(effect); + amount = effect.amount.copy(); + preventable = effect.preventable; + } + + @Override + public DamageTargetControllerEffect copy() { + return new DamageTargetControllerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if(permanent != null) { + Player targetController = game.getPlayer(permanent.getControllerId()); + if(targetController != null) { + targetController.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable); + } + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + String text = "{this} deals " + amount.getMessage() + " damage to target " + + mode.getTargets().get(0).getTargetName() + "'s controller"; + if(!preventable) { + text += ". The damage can't be prevented"; + } + return text; + } +} From 16116ea0dad8d097e7aae1e2166b2c0046b533c7 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 25 Jul 2015 21:10:54 +0300 Subject: [PATCH 03/18] Add DiscardOntoBattlefieldEffect and use it for existing cards. Implement card: Dodecapod --- .../src/mage/sets/apocalypse/Dodecapod.java | 97 +++++++++++++++++++ .../mage/sets/magic2011/ObstinateBaloth.java | 82 +++------------- .../sets/returntoravnica/LoxodonSmiter.java | 67 +------------ .../mage/sets/shadowmoor/WiltLeafLiege.java | 74 +------------- .../src/mage/sets/timeshifted/Dodecapod.java | 54 +++++++++++ .../common/DiscardOntoBattlefieldEffect.java | 96 ++++++++++++++++++ 6 files changed, 265 insertions(+), 205 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/Dodecapod.java create mode 100644 Mage.Sets/src/mage/sets/timeshifted/Dodecapod.java create mode 100644 Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/Dodecapod.java b/Mage.Sets/src/mage/sets/apocalypse/Dodecapod.java new file mode 100644 index 00000000000..28c78bf997a --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/Dodecapod.java @@ -0,0 +1,97 @@ +/* + * 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.apocalypse; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DiscardOntoBattlefieldEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LoneFox + */ +public class Dodecapod extends CardImpl { + + public Dodecapod(UUID ownerId) { + super(ownerId, 134, "Dodecapod", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "APC"; + this.subtype.add("Golem"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // If a spell or ability an opponent controls causes you to discard Dodecapod, put it onto the battlefield with two +1/+1 counters on it instead of putting it into your graveyard. + this.addAbility(new SimpleStaticAbility(Zone.HAND, new DodecapodEffect())); + + } + + public Dodecapod(final Dodecapod card) { + super(card); + } + + @Override + public Dodecapod copy() { + return new Dodecapod(this); + } +} + + +class DodecapodEffect extends DiscardOntoBattlefieldEffect { + + public DodecapodEffect() { + super(); + staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield with two +1/+1 counters on it instead of putting it into your graveyard"; + } + + public DodecapodEffect(final DodecapodEffect effect) { + super(effect); + } + + @Override + public DodecapodEffect copy() { + return new DodecapodEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if(super.replaceEvent(event, source, game)) { + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)).apply(game, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/magic2011/ObstinateBaloth.java b/Mage.Sets/src/mage/sets/magic2011/ObstinateBaloth.java index f4675b3bc48..b4ec4152be9 100644 --- a/Mage.Sets/src/mage/sets/magic2011/ObstinateBaloth.java +++ b/Mage.Sets/src/mage/sets/magic2011/ObstinateBaloth.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -29,25 +29,15 @@ package mage.sets.magic2011; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DiscardOntoBattlefieldEffect; import mage.abilities.effects.common.GainLifeEffect; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.stack.StackObject; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; /** * @@ -65,9 +55,9 @@ public class ObstinateBaloth extends CardImpl { // When Obstinate Baloth enters the battlefield, you gain 4 life. this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(4), false)); - + // If a spell or ability an opponent controls causes you to discard Obstinate Baloth, put it onto the battlefield instead of putting it into your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.HAND, new ObstinateBalothEffect())); + this.addAbility(new SimpleStaticAbility(Zone.HAND, new DiscardOntoBattlefieldEffect())); } public ObstinateBaloth(final ObstinateBaloth card) { @@ -81,53 +71,3 @@ public class ObstinateBaloth extends CardImpl { } -class ObstinateBalothEffect extends ReplacementEffectImpl { - - public ObstinateBalothEffect() { - super(Duration.EndOfGame, Outcome.PutCardInPlay); - staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield instead of putting it into your graveyard"; - } - - public ObstinateBalothEffect(final ObstinateBalothEffect effect) { - super(effect); - } - - @Override - public ObstinateBalothEffect copy() { - return new ObstinateBalothEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zcEvent = (ZoneChangeEvent) event; - if (zcEvent.getFromZone() == Zone.HAND && zcEvent.getToZone() == Zone.GRAVEYARD) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - if (spell != null && game.getOpponents(source.getControllerId()).contains(spell.getControllerId())) { - return true; - } - } - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null) { - if (owner.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId())) { - return true; - } - } - } - return false; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/returntoravnica/LoxodonSmiter.java b/Mage.Sets/src/mage/sets/returntoravnica/LoxodonSmiter.java index 3ec4636e9c7..e127fad6560 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/LoxodonSmiter.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/LoxodonSmiter.java @@ -32,19 +32,12 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CantBeCounteredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.cards.Card; +import mage.abilities.effects.common.DiscardOntoBattlefieldEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.stack.StackObject; -import mage.players.Player; /** * @@ -67,7 +60,7 @@ public class LoxodonSmiter extends CardImpl { this.addAbility(new CantBeCounteredAbility()); // If a spell or ability an opponent controls causes you to discard Loxodon Smiter, put it onto the battlefield instead of putting it into your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.HAND, new LoxodonSmiterEffect())); + this.addAbility(new SimpleStaticAbility(Zone.HAND, new DiscardOntoBattlefieldEffect())); } public LoxodonSmiter(final LoxodonSmiter card) { @@ -79,59 +72,3 @@ public class LoxodonSmiter extends CardImpl { return new LoxodonSmiter(this); } } - -class LoxodonSmiterEffect extends ReplacementEffectImpl { - - public LoxodonSmiterEffect() { - super(Duration.EndOfGame, Outcome.PutCardInPlay); - staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield instead of putting it into your graveyard"; - } - - public LoxodonSmiterEffect(final LoxodonSmiterEffect effect) { - super(effect); - } - - @Override - public LoxodonSmiterEffect copy() { - return new LoxodonSmiterEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zcEvent = (ZoneChangeEvent) event; - if (zcEvent.getFromZone() == Zone.HAND && zcEvent.getToZone() == Zone.GRAVEYARD) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - if (spell != null && game.getOpponents(source.getControllerId()).contains(spell.getControllerId())) { - return true; - } - } - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - if (card.putOntoBattlefield(game, Zone.HAND, source.getSourceId(), player.getId())) { - return true; - } - } - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return apply(game, source); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WiltLeafLiege.java b/Mage.Sets/src/mage/sets/shadowmoor/WiltLeafLiege.java index 373f7aaa411..a8c72991aab 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/WiltLeafLiege.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/WiltLeafLiege.java @@ -30,27 +30,18 @@ package mage.sets.shadowmoor; import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DiscardOntoBattlefieldEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.stack.StackObject; -import mage.players.Player; /** * @@ -66,7 +57,7 @@ public class WiltLeafLiege extends CardImpl { filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); filterWhite.add(new ControllerPredicate(TargetController.YOU)); } - + public WiltLeafLiege(UUID ownerId) { super(ownerId, 245, "Wilt-Leaf Liege", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{G/W}{G/W}{G/W}"); this.expansionSetCode = "SHM"; @@ -77,11 +68,11 @@ public class WiltLeafLiege extends CardImpl { this.toughness = new MageInt(4); // Other green creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(new StaticValue(1), new StaticValue(1), Duration.WhileOnBattlefield, filterGreen, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filterGreen, true))); // Other white creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(new StaticValue(1), new StaticValue(1), Duration.WhileOnBattlefield, filterWhite, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filterWhite, true))); // If a spell or ability an opponent controls causes you to discard Wilt-Leaf Liege, put it onto the battlefield instead of putting it into your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.HAND, new WiltLeafLiegeEffect())); + this.addAbility(new SimpleStaticAbility(Zone.HAND, new DiscardOntoBattlefieldEffect())); } public WiltLeafLiege(final WiltLeafLiege card) { @@ -93,58 +84,3 @@ public class WiltLeafLiege extends CardImpl { return new WiltLeafLiege(this); } } - -class WiltLeafLiegeEffect extends ReplacementEffectImpl { - - public WiltLeafLiegeEffect() { - super(Duration.EndOfGame, Outcome.PutCardInPlay); - staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield instead of putting it into your graveyard"; - } - - public WiltLeafLiegeEffect(final WiltLeafLiegeEffect effect) { - super(effect); - } - - @Override - public WiltLeafLiegeEffect copy() { - return new WiltLeafLiegeEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType().equals(GameEvent.EventType.ZONE_CHANGE); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zcEvent = (ZoneChangeEvent) event; - if (zcEvent.getFromZone() == Zone.HAND && zcEvent.getToZone() == Zone.GRAVEYARD) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - if (spell != null && game.getOpponents(source.getControllerId()).contains(spell.getControllerId())) { - return true; - } - } - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - Player player = game.getPlayer(card.getOwnerId()); - if (player != null) { - card.putOntoBattlefield(game, Zone.HAND, source.getSourceId(), player.getId()); - return true; - } - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return apply(game, source); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/timeshifted/Dodecapod.java b/Mage.Sets/src/mage/sets/timeshifted/Dodecapod.java new file mode 100644 index 00000000000..73f7ddd1063 --- /dev/null +++ b/Mage.Sets/src/mage/sets/timeshifted/Dodecapod.java @@ -0,0 +1,54 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.timeshifted; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class Dodecapod extends mage.sets.apocalypse.Dodecapod { + + public Dodecapod(UUID ownerId) { + super(ownerId); + this.cardNumber = 108; + this.expansionSetCode = "TSB"; + this.rarity = Rarity.SPECIAL; + } + + public Dodecapod(final Dodecapod card) { + super(card); + } + + @Override + public Dodecapod copy() { + return new Dodecapod(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java new file mode 100644 index 00000000000..137b33c5145 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java @@ -0,0 +1,96 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent.EventType; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.stack.StackObject; +import mage.players.Player; + +/** + * + * @author LoneFox + */ +public class DiscardOntoBattlefieldEffect extends ReplacementEffectImpl { + + public DiscardOntoBattlefieldEffect() { + super(Duration.EndOfGame, Outcome.PutCardInPlay); + staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield instead of putting it into your graveyard"; + } + + public DiscardOntoBattlefieldEffect(final DiscardOntoBattlefieldEffect effect) { + super(effect); + } + + @Override + public DiscardOntoBattlefieldEffect copy() { + return new DiscardOntoBattlefieldEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getSourceId())) { + ZoneChangeEvent zcEvent = (ZoneChangeEvent) event; + if (zcEvent.getFromZone() == Zone.HAND && zcEvent.getToZone() == Zone.GRAVEYARD) { + StackObject spell = game.getStack().getStackObject(event.getSourceId()); + if (spell != null && game.getOpponents(source.getControllerId()).contains(spell.getControllerId())) { + return true; + } + } + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Card card = game.getCard(source.getSourceId()); + if (card != null) { + Player owner = game.getPlayer(card.getOwnerId()); + if (owner != null) { + if (owner.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId())) { + return true; + } + } + } + return false; + } + +} From 77e53d6bbd01f3558f9f8ed563b56acd832f726c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 26 Jul 2015 11:49:31 +0300 Subject: [PATCH 04/18] Add PlayWithHandRevealedEffect and use it for existing cards. Implement cards: Revelation, Seer's Vision, and Wandering Eye --- .../src/mage/sets/iceage/ZursWeirding.java | 55 ++------- .../src/mage/sets/invasion/SeersVision.java | 70 +++++++++++ .../src/mage/sets/legends/Revelation.java | 62 ++++++++++ .../src/mage/sets/nemesis/WanderingEye.java | 68 +++++++++++ .../src/mage/sets/urzassaga/Telepathy.java | 54 ++------- .../PlayWithHandRevealedEffect.java | 114 ++++++++++++++++++ 6 files changed, 336 insertions(+), 87 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/invasion/SeersVision.java create mode 100644 Mage.Sets/src/mage/sets/legends/Revelation.java create mode 100644 Mage.Sets/src/mage/sets/nemesis/WanderingEye.java create mode 100644 Mage/src/mage/abilities/effects/common/continuous/PlayWithHandRevealedEffect.java diff --git a/Mage.Sets/src/mage/sets/iceage/ZursWeirding.java b/Mage.Sets/src/mage/sets/iceage/ZursWeirding.java index ebee9cfe3df..7d722651097 100644 --- a/Mage.Sets/src/mage/sets/iceage/ZursWeirding.java +++ b/Mage.Sets/src/mage/sets/iceage/ZursWeirding.java @@ -31,17 +31,16 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; +import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -59,8 +58,8 @@ public class ZursWeirding extends CardImpl { this.expansionSetCode = "ICE"; // Players play with their hands revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayerRevealHandCardsEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.ANY))); + // If a player would draw a card, he or she reveals it instead. Then any other player may pay 2 life. If a player does, put that card into its owner's graveyard. Otherwise, that player draws a card. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ZursWeirdingReplacementEffect())); } @@ -81,7 +80,7 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { super(Duration.WhileOnBattlefield, Outcome.Neutral); this.staticText = "If a player would draw a card, he or she reveals it instead. Then any other player may pay 2 life. If a player does, put that card into its owner's graveyard. Otherwise, that player draws a card."; } - + public ZursWeirdingReplacementEffect(final ZursWeirdingReplacementEffect effect) { super(effect); } @@ -90,7 +89,7 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { public ZursWeirdingReplacementEffect copy() { return new ZursWeirdingReplacementEffect(this); } - + @Override public boolean apply(Game game, Ability source) { return true; @@ -105,7 +104,7 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { if (card != null) { // reveals it instead player.revealCards(sourceObject.getIdName() + " next draw of " + player.getName() + " (" + game.getTurnNum()+"|"+game.getPhase().getType() +")", new CardsImpl(card), game); - + // Then any other player may pay 2 life. If a player does, put that card into its owner's graveyard PlayerList playerList = game.getPlayerList().copy(); playerList.setCurrent(player.getId()); @@ -116,14 +115,14 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { currentPlayer.getLife() >= 2 && currentPlayer.chooseUse(Outcome.Benefit, message, source, game)) { currentPlayer.loseLife(2, game); - player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); + player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); // game.getState().getRevealed().reset(); return true; } - + currentPlayer = playerList.getNext(game); } - + // game.getState().getRevealed().reset(); } } @@ -132,43 +131,11 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DRAW_CARD; - } + } @Override public boolean applies(GameEvent event, Ability source, Game game) { return true; } - -} -class PlayerRevealHandCardsEffect extends ContinuousEffectImpl { - - public PlayerRevealHandCardsEffect() { - super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); - staticText = "Players play with their hands revealed"; - } - - public PlayerRevealHandCardsEffect(final PlayerRevealHandCardsEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerID : controller.getInRange()) { - Player player = game.getPlayer(playerID); - if (player != null) { - player.revealCards(player.getName() + "'s hand cards", player.getHand(), game, false); - } - } - return true; - } - return false; - } - - @Override - public PlayerRevealHandCardsEffect copy() { - return new PlayerRevealHandCardsEffect(this); - } } diff --git a/Mage.Sets/src/mage/sets/invasion/SeersVision.java b/Mage.Sets/src/mage/sets/invasion/SeersVision.java new file mode 100644 index 00000000000..e2cb73ad3bb --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/SeersVision.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.invasion; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.target.TargetPlayer; + +/** + * + * @author LoneFox + */ +public class SeersVision extends CardImpl { + + public SeersVision(UUID ownerId) { + super(ownerId, 270, "Seer's Vision", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{B}"); + this.expansionSetCode = "INV"; + + // Your opponents play with their hands revealed. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.OPPONENT))); + // Sacrifice Seer's Vision: Look at target player's hand and choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new DiscardCardYouChooseTargetEffect(), new SacrificeSourceCost()); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + public SeersVision(final SeersVision card) { + super(card); + } + + @Override + public SeersVision copy() { + return new SeersVision(this); + } +} diff --git a/Mage.Sets/src/mage/sets/legends/Revelation.java b/Mage.Sets/src/mage/sets/legends/Revelation.java new file mode 100644 index 00000000000..bf24c939253 --- /dev/null +++ b/Mage.Sets/src/mage/sets/legends/Revelation.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.legends; + +import java.util.UUID; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class Revelation extends CardImpl { + + public Revelation(UUID ownerId) { + super(ownerId, 116, "Revelation", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{G}"); + this.expansionSetCode = "LEG"; + this.supertype.add("World"); + + // Players play with their hands revealed. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.ANY))); + } + + public Revelation(final Revelation card) { + super(card); + } + + @Override + public Revelation copy() { + return new Revelation(this); + } +} diff --git a/Mage.Sets/src/mage/sets/nemesis/WanderingEye.java b/Mage.Sets/src/mage/sets/nemesis/WanderingEye.java new file mode 100644 index 00000000000..04c4fa1c665 --- /dev/null +++ b/Mage.Sets/src/mage/sets/nemesis/WanderingEye.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.nemesis; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class WanderingEye extends CardImpl { + + public WanderingEye(UUID ownerId) { + super(ownerId, 50, "Wandering Eye", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{U}"); + this.expansionSetCode = "NMS"; + this.subtype.add("Illusion"); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Players play with their hands revealed. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.ANY))); + } + + public WanderingEye(final WanderingEye card) { + super(card); + } + + @Override + public WanderingEye copy() { + return new WanderingEye(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/Telepathy.java b/Mage.Sets/src/mage/sets/urzassaga/Telepathy.java index 037fe814b77..e71a4cbb551 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Telepathy.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Telepathy.java @@ -27,16 +27,18 @@ */ package mage.sets.urzassaga; -import mage.constants.*; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.cards.CardImpl; -import mage.game.Game; -import mage.players.Player; - import java.util.UUID; import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; /** * @@ -49,7 +51,7 @@ public class Telepathy extends CardImpl { this.expansionSetCode = "USG"; // Your opponents play with their hands revealed. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OpponentsPlayWithTheTopCardRevealedEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithHandRevealedEffect(TargetController.OPPONENT))); } public Telepathy(final Telepathy card) { @@ -61,37 +63,3 @@ public class Telepathy extends CardImpl { return new Telepathy(this); } } - -class OpponentsPlayWithTheTopCardRevealedEffect extends ContinuousEffectImpl { - - public OpponentsPlayWithTheTopCardRevealedEffect() { - super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); - staticText = "Your opponents play with their hands revealed"; - } - - public OpponentsPlayWithTheTopCardRevealedEffect(final OpponentsPlayWithTheTopCardRevealedEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null) { - for (UUID opponentId : game.getOpponents(controller.getId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - controller.revealCards(sourceObject.getIdName() + " " + opponent.getName(), opponent.getHand(), game, false); - } - } - return true; - } - return false; - } - - @Override - public OpponentsPlayWithTheTopCardRevealedEffect copy() { - return new OpponentsPlayWithTheTopCardRevealedEffect(this); - } - -} diff --git a/Mage/src/mage/abilities/effects/common/continuous/PlayWithHandRevealedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/PlayWithHandRevealedEffect.java new file mode 100644 index 00000000000..ebbb47260b8 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/continuous/PlayWithHandRevealedEffect.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common.continuous; + +import java.util.ArrayList; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.TargetController; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LoneFox + */ +public class PlayWithHandRevealedEffect extends ContinuousEffectImpl { + + private TargetController who; + + public PlayWithHandRevealedEffect(TargetController who) { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment); + this.who = who; + } + + public PlayWithHandRevealedEffect(final PlayWithHandRevealedEffect effect) { + super(effect); + who = effect.who; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Iterable affectedPlayers; + switch(who) { + case ANY: + affectedPlayers = controller.getInRange(); + break; + case OPPONENT: + affectedPlayers = game.getOpponents(source.getControllerId()); + break; + case YOU: + ArrayList tmp = new ArrayList(); + tmp.add(source.getControllerId()); + affectedPlayers = tmp; + break; + default: + return false; + } + + for(UUID playerID: affectedPlayers) { + Player player = game.getPlayer(playerID); + if (player != null) { + player.revealCards(player.getName() + "'s hand cards", player.getHand(), game, false); + } + } + return true; + } + return false; + } + + @Override + public PlayWithHandRevealedEffect copy() { + return new PlayWithHandRevealedEffect(this); + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + switch(who) { + case ANY: + return "Players play with their hands revealed"; + case OPPONENT: + return "Your opponents play with their hands revealed"; + case YOU: + return "Play with your hand revealed"; + default: + return "Unknown TargetController for PlayWithHandRevealedEffect"; + } + } +} From 085914f4e4570a6322c78e3ece94ebaebd7bee21 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 26 Jul 2015 12:04:15 +0300 Subject: [PATCH 05/18] Fix missing sacrifice cost of the last ability of Wintermoon Mesa --- Mage.Sets/src/mage/sets/prophecy/WintermoonMesa.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/prophecy/WintermoonMesa.java b/Mage.Sets/src/mage/sets/prophecy/WintermoonMesa.java index 959aa4e37c9..41e285b6b91 100644 --- a/Mage.Sets/src/mage/sets/prophecy/WintermoonMesa.java +++ b/Mage.Sets/src/mage/sets/prophecy/WintermoonMesa.java @@ -31,6 +31,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.TapTargetEffect; @@ -58,6 +59,7 @@ public class WintermoonMesa extends CardImpl { // {2}, {tap}, Sacrifice Wintermoon Mesa: Tap two target lands. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl<>("{2}")); ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetLandPermanent(2)); this.addAbility(ability); } From 145bd39175266bd0573260dcf7bb3a23faab34fb Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 26 Jul 2015 12:25:31 +0300 Subject: [PATCH 06/18] Implement cards: Juntu Stakes, Urborg Phantom, and Vodalian Mystic --- .../mage/sets/apocalypse/VodalianMystic.java | 82 +++++++++++++++++++ .../src/mage/sets/invasion/JuntuStakes.java | 71 ++++++++++++++++ .../src/mage/sets/invasion/UrborgPhantom.java | 79 ++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/VodalianMystic.java create mode 100644 Mage.Sets/src/mage/sets/invasion/JuntuStakes.java create mode 100644 Mage.Sets/src/mage/sets/invasion/UrborgPhantom.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/VodalianMystic.java b/Mage.Sets/src/mage/sets/apocalypse/VodalianMystic.java new file mode 100644 index 00000000000..a46c45b9817 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/VodalianMystic.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.apocalypse; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.continuous.BecomesColorTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.target.TargetSpell; + +/** + * + * @author LoneFox + */ +public class VodalianMystic extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("instant or sorcery spell"); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public VodalianMystic(UUID ownerId) { + super(ownerId, 33, "Vodalian Mystic", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetCode = "APC"; + this.subtype.add("Merfolk"); + this.subtype.add("Wizard"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Target instant or sorcery spell becomes the color of your choice. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(Duration.WhileOnStack), new TapSourceCost()); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + } + + public VodalianMystic(final VodalianMystic card) { + super(card); + } + + @Override + public VodalianMystic copy() { + return new VodalianMystic(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/JuntuStakes.java b/Mage.Sets/src/mage/sets/invasion/JuntuStakes.java new file mode 100644 index 00000000000..785f9e98b67 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/JuntuStakes.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.invasion; + +import java.util.UUID; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.Filter; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; + +/** + * + * @author LoneFox + */ +public class JuntuStakes extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures with power 1 or less"); + + static { + filter.add(new PowerPredicate(Filter.ComparisonType.LessThan, 2)); + } + + public JuntuStakes(UUID ownerId) { + super(ownerId, 304, "Juntu Stakes", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.expansionSetCode = "INV"; + + // Creatures with power 1 or less don't untap during their controllers' untap steps. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepAllEffect(Duration.WhileOnBattlefield, TargetController.ANY, filter))); + } + + public JuntuStakes(final JuntuStakes card) { + super(card); + } + + @Override + public JuntuStakes copy() { + return new JuntuStakes(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/UrborgPhantom.java b/Mage.Sets/src/mage/sets/invasion/UrborgPhantom.java new file mode 100644 index 00000000000..f308c862049 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/UrborgPhantom.java @@ -0,0 +1,79 @@ +/* + * 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.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CantBlockAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventCombatDamageBySourceEffect; +import mage.abilities.effects.common.PreventCombatDamageToSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class UrborgPhantom extends CardImpl { + + public UrborgPhantom(UUID ownerId) { + super(ownerId, 132, "Urborg Phantom", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{B}"); + this.expansionSetCode = "INV"; + this.subtype.add("Spirit"); + this.subtype.add("Minion"); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Urborg Phantom can't block. + this.addAbility(new CantBlockAbility()); + // {U}: Prevent all combat damage that would be dealt to and dealt by Urborg Phantom this turn. + Effect effect = new PreventCombatDamageToSourceEffect(Duration.EndOfTurn); + effect.setText("Prevent all combat damage that would be dealt to"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}")); + effect = new PreventCombatDamageBySourceEffect(Duration.EndOfTurn); + effect.setText("and dealt by {this} this turn"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public UrborgPhantom(final UrborgPhantom card) { + super(card); + } + + @Override + public UrborgPhantom copy() { + return new UrborgPhantom(this); + } +} From 4d798e81b8ca7eddb2f23bea759ae109494d3a61 Mon Sep 17 00:00:00 2001 From: klayhamn Date: Sun, 26 Jul 2015 19:30:15 +0300 Subject: [PATCH 07/18] add thrumming stone and surging dementia --- .../cards/abilities/keywords/RippleTest.java | 112 +++++++++++++++++ .../effects/keyword/RippleEffect.java | 118 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java create mode 100644 Mage/src/mage/abilities/effects/keyword/RippleEffect.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java new file mode 100644 index 00000000000..3048249b38c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java @@ -0,0 +1,112 @@ +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 klayhamn + */ +public class RippleTest extends CardTestPlayerBase { + + /** + * 702.59.Ripple + * 702.59a Ripple is a triggered ability that functions only while the card with ripple is on the stack. “Ripple N” means + * “When you cast this spell, you may reveal the top N cards of your library, or, if there are fewer than N cards in your + * library, you may reveal all the cards in your library. If you reveal cards from your library this way, you may cast any + * of those cards with the same name as this spell without paying their mana costs, then put all revealed cards not cast + * this way on the bottom of your library in any order.” + * 702.59b If a spell has multiple instances of ripple, each triggers separately. + */ + + @Test + public void testRippleWhenSameCardNotFound() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.HAND, playerA, "Surging Dementia",2 ); + addCard(Zone.LIBRARY, playerA, "Swamp", 4); + + + addCard(Zone.HAND, playerB, "Island", 3); + addCard(Zone.LIBRARY, playerB, "Island", 3); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB); + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.END_TURN); + + execute(); + + assertHandCount(playerB, 3); // should have 2 less + assertHandCount(playerA, 1); // after cast, one remains + + assertGraveyardCount(playerA, "Surging Dementia", 1); + assertGraveyardCount(playerB, "Island", 1); + } + + @Test + public void testRippleWhenSameCardFoundOnce() { + + removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.HAND, playerA, "Surging Dementia",2 ); + addCard(Zone.LIBRARY, playerA, "Surging Dementia",1); + addCard(Zone.LIBRARY, playerA, "Swamp", 3); + + + addCard(Zone.HAND, playerB, "Island", 3); + addCard(Zone.LIBRARY, playerB, "Island", 3); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB); + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.END_TURN); + + execute(); + + assertHandCount(playerB, 2); // should have 2 less + assertHandCount(playerA, 1); // after cast, none remain + assertGraveyardCount(playerA, "Surging Dementia", 2); + assertGraveyardCount(playerB, "Island", 2); + + } + + @Test + public void testRippleWhenSameCardFoundMoreThanOnce() { + + + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + addCard(Zone.HAND, playerA, "Surging Dementia",2 ); + + addCard(Zone.LIBRARY, playerA, "Surging Dementia",1); + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + addCard(Zone.LIBRARY, playerA, "Surging Dementia",1); + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + + + addCard(Zone.HAND, playerB, "Island", 3); + addCard(Zone.LIBRARY, playerB, "Island", 3); + + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB); + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.END_TURN); + + execute(); + + assertHandCount(playerB, 1); // should have 2 less + assertHandCount(playerA, 1); // after cast, none remain + assertGraveyardCount(playerA, "Surging Dementia", 3); + assertGraveyardCount(playerB, "Island", 3); + } +} diff --git a/Mage/src/mage/abilities/effects/keyword/RippleEffect.java b/Mage/src/mage/abilities/effects/keyword/RippleEffect.java new file mode 100644 index 00000000000..4ad87f442d2 --- /dev/null +++ b/Mage/src/mage/abilities/effects/keyword/RippleEffect.java @@ -0,0 +1,118 @@ +package mage.abilities.effects.keyword; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author klayhamn + */ +public class RippleEffect extends OneShotEffect { + + protected int rippleNumber; + protected boolean isTargetSelf; // is the source of the ripple also the target of the ripple? + + public RippleEffect(int rippleNumber) { + this(rippleNumber, true); // by default, the source is also the target + } + + public RippleEffect(int rippleNumber, boolean isTargetSelf) { + super(Outcome.PlayForFree); + this.rippleNumber = rippleNumber; + this.isTargetSelf = isTargetSelf; + this.setText(); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (player != null) { + if (!player.chooseUse(Outcome.Neutral, "Reveal "+ rippleNumber + " cards from the top of your library?", source, game )){ + return false; //fizzle + } + + Cards cards = new CardsImpl(); + int count = Math.min(rippleNumber, player.getLibrary().size()); + if (count == 0) { + return true; + } + player.revealCards(sourceObject.getIdName(), cards, game); + for (int i = 0; i < count; i++) { + Card card = player.getLibrary().removeFromTop(game); + cards.add(card); + } + + // Select cards with the same name as the spell on which the ripple effect applies + // FIXME: I'm not sure the "isTargetSelf" flag is the most elegant solution + String cardNameToRipple; + if (isTargetSelf) { // if the ripple applies to the same card that triggered it + cardNameToRipple = sourceObject.getName(); + } else { // if the ripple is caused by something else (e.g. Thrumming Stone) + if (targetPointer == null) { + return true; // this might be possible if the "rememberSource" param wasn't used (which would constitute a bug) + } + UUID triggeringSpellID = targetPointer.getFirst(game, source); + Spell spellOnStack = game.getStack().getSpell(triggeringSpellID); + if (spellOnStack == null) { + return true; // spell was countered or exiled, effect should fizzle + } + cardNameToRipple = spellOnStack.getName(); + } + + FilterCard sameNameFilter = new FilterCard("card(s) with the name: \"" + cardNameToRipple + "\" to cast without paying their mana cost"); + sameNameFilter.add(new NamePredicate(cardNameToRipple)); + TargetCard target1 = new TargetCard(Zone.LIBRARY, sameNameFilter); + target1.setRequired(false); + + // Choose cards to play for free + while (player.isInGame() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) { + Card card = cards.get(target1.getFirstTarget(), game); + if (card != null) { + player.cast(card.getSpellAbility(), game, true); + cards.remove(card); + } + target1.clearChosen(); + } + // move cards that weren't cast to the bottom of the library + player.putCardsOnBottomOfLibrary(cards, game, source, true); + // do we need to fire an event here? there is nothing that listens to ripple so far... + return true; + } + + return false; + } + + public RippleEffect(final RippleEffect effect) { + super(effect); + this.rippleNumber = effect.rippleNumber; + this.isTargetSelf = effect.isTargetSelf; + } + + @Override + public RippleEffect copy() { + return new RippleEffect(this); + } + + private void setText() { + StringBuilder sb = new StringBuilder("Ripple ").append(rippleNumber); + sb.append(". (You may reveal the top "); + sb.append(CardUtil.numberToText(rippleNumber)); + sb.append(" cards of your library. You may cast any revealed cards with the same name as this spell without paying their mana costs. Put the rest on the bottom of your library.)"); + staticText = sb.toString(); + } +} From 9621959922139462d928271f236e7866c4fca520 Mon Sep 17 00:00:00 2001 From: klayhamn Date: Sun, 26 Jul 2015 19:33:24 +0300 Subject: [PATCH 08/18] add thrumming stone and surging dementia --- .../mage/sets/coldsnap/SurgingDementia.java | 67 +++++++++++++++++++ .../mage/sets/coldsnap/ThrummingStone.java | 64 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/coldsnap/SurgingDementia.java create mode 100644 Mage.Sets/src/mage/sets/coldsnap/ThrummingStone.java diff --git a/Mage.Sets/src/mage/sets/coldsnap/SurgingDementia.java b/Mage.Sets/src/mage/sets/coldsnap/SurgingDementia.java new file mode 100644 index 00000000000..72a52c42be7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/SurgingDementia.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.coldsnap; + +import java.util.UUID; + +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.effects.keyword.RippleEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.TargetPlayer; + +/** + * + * @author klayhamn + */ +public class SurgingDementia extends CardImpl { + + public SurgingDementia(UUID ownerId) { + super(ownerId, 72, "Surging Dementia", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{B}"); + this.expansionSetCode = "CSP"; + + // Ripple 4 + this.getSpellAbility().addEffect(new RippleEffect(4)); + // Target player discards a card. + this.getSpellAbility().getEffects().add(new DiscardTargetEffect(1)); + this.getSpellAbility().getTargets().add(new TargetPlayer()); + } + + public SurgingDementia(final SurgingDementia card) { + super(card); + } + + @Override + public SurgingDementia copy() { + return new SurgingDementia(this); + } +} diff --git a/Mage.Sets/src/mage/sets/coldsnap/ThrummingStone.java b/Mage.Sets/src/mage/sets/coldsnap/ThrummingStone.java new file mode 100644 index 00000000000..26781804359 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/ThrummingStone.java @@ -0,0 +1,64 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.coldsnap; + +import java.util.UUID; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.keyword.RippleEffect; +import mage.cards.CardImpl; +import mage.constants.*; +import mage.filter.FilterSpell; + +/** + * + * @author klayhamn + */ +public class ThrummingStone extends CardImpl { + + //applies to all spells + private static final FilterSpell anySpellFilter = new FilterSpell(); + + public ThrummingStone(UUID ownerId) { + super(ownerId, 142, "Thrumming Stone", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetCode = "CSP"; + this.supertype.add("Legendary"); + + addAbility(new SpellCastControllerTriggeredAbility(new RippleEffect(4, false), anySpellFilter, false, true)); + } + + public ThrummingStone(final ThrummingStone card) { + super(card); + } + + @Override + public ThrummingStone copy() { + return new ThrummingStone(this); + } + +} + From 50b1085ebb12f39fb5ceb7f5941d49c583fc7cca Mon Sep 17 00:00:00 2001 From: klayhamn Date: Mon, 27 Jul 2015 02:01:57 +0300 Subject: [PATCH 09/18] fix RippleEffect so that it still triggers even when original spell is exiled or countered --- .../effects/keyword/RippleEffect.java | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/Mage/src/mage/abilities/effects/keyword/RippleEffect.java b/Mage/src/mage/abilities/effects/keyword/RippleEffect.java index 4ad87f442d2..db5dd8a7181 100644 --- a/Mage/src/mage/abilities/effects/keyword/RippleEffect.java +++ b/Mage/src/mage/abilities/effects/keyword/RippleEffect.java @@ -16,8 +16,6 @@ import mage.players.Player; import mage.target.TargetCard; import mage.util.CardUtil; -import java.util.UUID; - /** * @author klayhamn */ @@ -37,39 +35,42 @@ public class RippleEffect extends OneShotEffect { this.setText(); } + public RippleEffect(final RippleEffect effect) { + super(effect); + this.rippleNumber = effect.rippleNumber; + this.isTargetSelf = effect.isTargetSelf; + } + + @Override + public RippleEffect copy() { + return new RippleEffect(this); + } + + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (player != null) { - if (!player.chooseUse(Outcome.Neutral, "Reveal "+ rippleNumber + " cards from the top of your library?", source, game )){ - return false; //fizzle + if (!player.chooseUse(Outcome.Neutral, "Reveal " + rippleNumber + " cards from the top of your library?", source, game)){ + return true; //fizzle } - Cards cards = new CardsImpl(); - int count = Math.min(rippleNumber, player.getLibrary().size()); - if (count == 0) { - return true; - } - player.revealCards(sourceObject.getIdName(), cards, game); - for (int i = 0; i < count; i++) { - Card card = player.getLibrary().removeFromTop(game); - cards.add(card); - } + cards.addAll(player.getLibrary().getTopCards(game, rippleNumber)); // pull top cards + player.revealCards(sourceObject.getIdName(), cards, game); // reveal the cards - // Select cards with the same name as the spell on which the ripple effect applies + // Find out which card should be rippled // FIXME: I'm not sure the "isTargetSelf" flag is the most elegant solution String cardNameToRipple; if (isTargetSelf) { // if the ripple applies to the same card that triggered it cardNameToRipple = sourceObject.getName(); } else { // if the ripple is caused by something else (e.g. Thrumming Stone) - if (targetPointer == null) { - return true; // this might be possible if the "rememberSource" param wasn't used (which would constitute a bug) + Spell spellOnStack = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spellOnStack == null) { // if the ripple target got countered or exiled + spellOnStack = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK); } - UUID triggeringSpellID = targetPointer.getFirst(game, source); - Spell spellOnStack = game.getStack().getSpell(triggeringSpellID); if (spellOnStack == null) { - return true; // spell was countered or exiled, effect should fizzle + return true; // should not happen? } cardNameToRipple = spellOnStack.getName(); } @@ -90,24 +91,12 @@ public class RippleEffect extends OneShotEffect { } // move cards that weren't cast to the bottom of the library player.putCardsOnBottomOfLibrary(cards, game, source, true); - // do we need to fire an event here? there is nothing that listens to ripple so far... return true; } return false; } - public RippleEffect(final RippleEffect effect) { - super(effect); - this.rippleNumber = effect.rippleNumber; - this.isTargetSelf = effect.isTargetSelf; - } - - @Override - public RippleEffect copy() { - return new RippleEffect(this); - } - private void setText() { StringBuilder sb = new StringBuilder("Ripple ").append(rippleNumber); sb.append(". (You may reveal the top "); From 37dbdeb07f6e2aba0ac6b9d85add6c0bb30dfa64 Mon Sep 17 00:00:00 2001 From: klayhamn Date: Mon, 27 Jul 2015 02:11:08 +0300 Subject: [PATCH 10/18] correct some incorrect documentation comments in ripple test --- .../cards/abilities/keywords/RippleTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java index 3048249b38c..4217495e540 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RippleTest.java @@ -39,11 +39,11 @@ public class RippleTest extends CardTestPlayerBase { execute(); - assertHandCount(playerB, 3); // should have 2 less + assertHandCount(playerB, 3); // should have 1 less assertHandCount(playerA, 1); // after cast, one remains - assertGraveyardCount(playerA, "Surging Dementia", 1); - assertGraveyardCount(playerB, "Island", 1); + assertGraveyardCount(playerA, "Surging Dementia", 1); // 1 cast + assertGraveyardCount(playerB, "Island", 1); // 1 discarded } @Test @@ -70,9 +70,9 @@ public class RippleTest extends CardTestPlayerBase { execute(); assertHandCount(playerB, 2); // should have 2 less - assertHandCount(playerA, 1); // after cast, none remain - assertGraveyardCount(playerA, "Surging Dementia", 2); - assertGraveyardCount(playerB, "Island", 2); + assertHandCount(playerA, 1); // after cast, one remains in hand + assertGraveyardCount(playerA, "Surging Dementia", 2); // 2 were cast + assertGraveyardCount(playerB, "Island", 2); // 2 were discarded } @@ -104,9 +104,9 @@ public class RippleTest extends CardTestPlayerBase { execute(); - assertHandCount(playerB, 1); // should have 2 less - assertHandCount(playerA, 1); // after cast, none remain - assertGraveyardCount(playerA, "Surging Dementia", 3); - assertGraveyardCount(playerB, "Island", 3); + assertHandCount(playerB, 1); // should have 3 less + assertHandCount(playerA, 1); // after cast, one remains in hand + assertGraveyardCount(playerA, "Surging Dementia", 3); // 3 were cast + assertGraveyardCount(playerB, "Island", 3); // 3 were discarded } } From 3e2963d21dafabb0b8588ed0d43e3ed0e2b24f9c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Mon, 27 Jul 2015 07:56:45 +0300 Subject: [PATCH 11/18] Fix a brainfart in Radiant Kavu's text --- Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java b/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java index b460f739148..e29f6f24da0 100644 --- a/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java +++ b/Mage.Sets/src/mage/sets/planeshift/RadiantKavu.java @@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public class RadiantKavu extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue and black creatures"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures and black creatures"); static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLUE), new ColorPredicate(ObjectColor.BLACK))); } From fc24fc6800d472891006c90548881203d100881c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Mon, 27 Jul 2015 07:57:52 +0300 Subject: [PATCH 12/18] Fix Warped Devotion to work whan a token is bounced --- .../src/mage/sets/eighthedition/WarpedDevotion.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java b/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java index 62f78c48d0f..777f332ff05 100644 --- a/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java +++ b/Mage.Sets/src/mage/sets/eighthedition/WarpedDevotion.java @@ -88,20 +88,17 @@ class WarpedDevotionTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zce = (ZoneChangeEvent)event; if(zce.getFromZone() == Zone.BATTLEFIELD && zce.getToZone() == Zone.HAND) { - Card card = game.getCard(event.getTargetId()); - if(card != null) { - for(Effect effect: getEffects()) { - effect.setTargetPointer(new FixedTarget(card.getOwnerId())); - } - return true; + for(Effect effect: getEffects()) { + effect.setTargetPointer(new FixedTarget(zce.getTarget().getOwnerId())); } + return true; } return false; } @Override public String getRule() { - return "Whenever a permanent is returned to a player's hand, that player discards a card"; + return "Whenever a permanent is returned to a player's hand, that player discards a card."; } } From b36f3a4a2be87865512b5bd86d7543c1a92a0199 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 07:58:51 +0200 Subject: [PATCH 13/18] * Mark of Mutiny - Fixed tooltip text. --- .../src/mage/sets/zendikar/MarkOfMutiny.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/sets/zendikar/MarkOfMutiny.java b/Mage.Sets/src/mage/sets/zendikar/MarkOfMutiny.java index 1f329686b98..d7b27e2f34f 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MarkOfMutiny.java +++ b/Mage.Sets/src/mage/sets/zendikar/MarkOfMutiny.java @@ -25,19 +25,19 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.zendikar; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Rarity; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; @@ -47,18 +47,28 @@ import mage.target.common.TargetCreaturePermanent; */ public class MarkOfMutiny extends CardImpl { - public MarkOfMutiny (UUID ownerId) { + public MarkOfMutiny(UUID ownerId) { super(ownerId, 137, "Mark of Mutiny", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{R}"); this.expansionSetCode = "ZEN"; + // Gain control of target creature until end of turn. + // Put a +1/+1 counter on it and untap it. + // That creature gains haste until end of turn. (It can attack and this turn.) this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new UntapTargetEffect()); + Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); + effect.setText("Put a +1/+1 counter on it"); + this.getSpellAbility().addEffect(effect); + effect = new UntapTargetEffect(); + effect.setText("and untap it"); + this.getSpellAbility().addEffect(effect); + effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + effect.setText("That creature gains haste until end of turn. (It can {T} attack and this turn.)"); + this.getSpellAbility().addEffect(effect); + } - public MarkOfMutiny (final MarkOfMutiny card) { + public MarkOfMutiny(final MarkOfMutiny card) { super(card); } From 0c9b2ebe71f1a188cf7a48d1bc2985f8dac74e19 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 27 Jul 2015 06:42:45 -0400 Subject: [PATCH 14/18] Updated list of formats Updated list of formats, accounting for All Standards and Historic Standards --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a7ee59046a3..42e70b4c04c 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,7 @@ You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforu * You can play either a two player duel or a multiplayer free-for-all game with up to 10 players. * Commander format (also up to 10 players). * Tiny Leaders duels. +* All Standards and Historic Standard formats. * There are two tournament types supported, which can be played with up to 16 players: * Elimination or swiss type handling * Booster (also Cube) draft tournaments (4-16) @@ -32,4 +33,4 @@ If you are interested in developing XMage, here are some useful resources: * [Developer Testing Tools](http://github.com/magefree/mage/wiki/Developer-Testing-Tools) * [Double Faced Cards](http://github.com/magefree/mage/wiki/Double-Faced-Cards) * [Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=4554) -* [Tournament Relevant Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=14062) \ No newline at end of file +* [Tournament Relevant Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=14062) From a3eb1f3a638de3e4f7cbed93b943b5e67bf966f2 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 27 Jul 2015 06:53:32 -0400 Subject: [PATCH 15/18] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 42e70b4c04c..d7ce960f8df 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforu * You can play either a two player duel or a multiplayer free-for-all game with up to 10 players. * Commander format (also up to 10 players). * Tiny Leaders duels. -* All Standards and Historic Standard formats. +* Super Standard and Historic Standard formats. * There are two tournament types supported, which can be played with up to 16 players: * Elimination or swiss type handling * Booster (also Cube) draft tournaments (4-16) From 808efbc762b23ba8b0e07c860fbaa4b64762e772 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 27 Jul 2015 07:06:34 -0400 Subject: [PATCH 16/18] Readme: simplifying feature list + minor edits --- readme.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index d7ce960f8df..590ae692157 100644 --- a/readme.md +++ b/readme.md @@ -7,20 +7,18 @@ There are public servers where you can play XMage against other players. You can You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforum.php?f=70). ## Features -* Deck editor to build your desired decks. -* There is a simple computer AI opponent available. -* You can play either a two player duel or a multiplayer free-for-all game with up to 10 players. -* Commander format (also up to 10 players). -* Tiny Leaders duels. -* Super Standard and Historic Standard formats. -* There are two tournament types supported, which can be played with up to 16 players: -* Elimination or swiss type handling -* Booster (also Cube) draft tournaments (4-16) -* Sealed (also from Cube) tournaments (2-16) +* Deck editor (load and save decks) +* Simple computer AI opponent +* Two player duel or a multiplayer free-for-all game with up to 10 players +* Supports special formats like Commander (up to 10 players), Cube, Tiny Leaders, Super Standard, Historic Standard +* There are two tournament types supported (elimination or swiss type handling), which can be played with up to 16 players: + * Booster (also Cube) draft tournaments (4-16) + * Sealed (also from Cube) tournaments (2-16) + ## Installation Download and install the [latest XMage release](http://XMage.de). -You will need to have the [Java Runtime Environment](http://java.com/en/) Version 7 or later. +You will need to have Version 7 or later of the [Java Runtime Environment](http://java.com/en/). Look [here](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) for more detailed instructions. [Here](http://github.com/magefree/mage/wiki/Release-changes) you can find a log of the latest changes. From dbff7bedb972893ec88abde52ab7fd264219f0a3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 17:21:52 +0200 Subject: [PATCH 17/18] * Some changes to skip turn handling (turn count and messages). --- .../org/mage/test/turnmod/SkipTurnTest.java | 63 +++++++++++++++++++ .../common/turn/SkipNextTurnSourceEffect.java | 17 ++++- Mage/src/mage/game/GameImpl.java | 31 ++------- Mage/src/mage/game/turn/Turn.java | 40 ++++++++++-- 4 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/turnmod/SkipTurnTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/turnmod/SkipTurnTest.java b/Mage.Tests/src/test/java/org/mage/test/turnmod/SkipTurnTest.java new file mode 100644 index 00000000000..71c88bf394d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/turnmod/SkipTurnTest.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.turnmod; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SkipTurnTest extends CardTestPlayerBase { + + @Test + public void testEaterOfDays() { + // At the beginning of your upkeep or whenever you cast a green spell, put a charge counter on Shrine of Boundless Growth. + // {T}, Sacrifice Shrine of Boundless Growth: Add {1} to your mana pool for each charge counter on Shrine of Boundless Growth. + addCard(Zone.HAND, playerA, "Shrine of Boundless Growth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + // Flying + // Trample + // When Eater of Days enters the battlefield, you skip your next two turns. + addCard(Zone.HAND, playerA, "Eater of Days", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eater of Days"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shrine of Boundless Growth"); + + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Eater of Days", 1); + assertPermanentCount(playerA, "Shrine of Boundless Growth", 1); + assertCounterCount("Shrine of Boundless Growth", CounterType.CHARGE, 1); + } +} diff --git a/Mage/src/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java b/Mage/src/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java index ff1431ad21f..511a0f1c388 100644 --- a/Mage/src/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/turn/SkipNextTurnSourceEffect.java @@ -10,20 +10,29 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; import mage.game.turn.TurnMod; +import mage.util.CardUtil; /** * * @author Mael */ public class SkipNextTurnSourceEffect extends OneShotEffect { - + + int numberOfTurns; + public SkipNextTurnSourceEffect() { + this(1); + } + + public SkipNextTurnSourceEffect(int numberOfTurns) { super(Outcome.Neutral); - staticText = "You skip your next turn"; + this.numberOfTurns = numberOfTurns; + staticText = "you skip your next " + (numberOfTurns == 1 ? "turn" : CardUtil.numberToText(numberOfTurns) + " turns"); } public SkipNextTurnSourceEffect(final SkipNextTurnSourceEffect effect) { super(effect); + this.numberOfTurns = effect.numberOfTurns; } @Override @@ -33,7 +42,9 @@ public class SkipNextTurnSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), true)); + for (int i = 0; i < numberOfTurns; i++) { + game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), true)); + } return true; } } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 858b5b8fff0..f3a878d2206 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -700,7 +700,6 @@ public abstract class GameImpl implements Game, Serializable { if (!playTurn(playerByOrder)) { break; } - state.setTurnNum(state.getTurnNum() + 1); } playExtraTurns(); playerByOrder = playerList.getNext(this); @@ -741,7 +740,6 @@ public abstract class GameImpl implements Game, Serializable { informPlayers(extraPlayer.getLogName() + " takes an extra turn"); } playTurn(extraPlayer); - state.setTurnNum(state.getTurnNum() + 1); } } extraTurn = getNextExtraTurn(); @@ -768,6 +766,7 @@ public abstract class GameImpl implements Game, Serializable { } private boolean playTurn(Player player) { + boolean skipTurn = false; do { if (executingRollback) { executingRollback = false; @@ -781,41 +780,23 @@ public abstract class GameImpl implements Game, Serializable { state.setActivePlayerId(player.getId()); saveRollBackGameState(); } - this.logStartOfTurn(player); if (checkStopOnTurnOption()) { return false; } - state.getTurn().play(this, player); + skipTurn = state.getTurn().play(this, player); } while (executingRollback); if (isPaused() || gameOver(null)) { return false; } - endOfTurn(); + if (!skipTurn) { + endOfTurn(); + state.setTurnNum(state.getTurnNum() + 1); + } return true; } - private void logStartOfTurn(Player player) { - StringBuilder sb = new StringBuilder("Turn ").append(state.getTurnNum()).append(" "); - sb.append(player.getLogName()); - sb.append(" ("); - int delimiter = this.getPlayers().size() - 1; - for (Player gamePlayer : this.getPlayers().values()) { - sb.append(gamePlayer.getLife()); - int poison = gamePlayer.getCounters().getCount(CounterType.POISON); - if (poison > 0) { - sb.append("[P:").append(poison).append("]"); - } - if (delimiter > 0) { - sb.append(" - "); - delimiter--; - } - } - sb.append(")"); - fireStatusEvent(sb.toString(), true); - } - private boolean checkStopOnTurnOption() { if (gameOptions.stopOnTurn != null && gameOptions.stopAtStep == PhaseStep.UNTAP) { if (gameOptions.stopOnTurn.equals(state.getTurnNum())) { diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index ec5d2f81ca1..30a367814ca 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.UUID; import mage.constants.PhaseStep; import mage.constants.TurnPhase; +import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -116,16 +117,24 @@ public class Turn implements Serializable { return null; } - public void play(Game game, Player activePlayer) { + /** + * + * @param game + * @param activePlayer + * @return true if turn is skipped + */ + public boolean play(Game game, Player activePlayer) { activePlayer.becomesActivePlayer(); this.setDeclareAttackersStepStarted(false); if (game.isPaused() || game.gameOver(null)) { - return; + return false; } if (game.getState().getTurnMods().skipTurn(activePlayer.getId())) { - return; + game.informPlayers(activePlayer.getLogName() + " skips his or her turn."); + return true; } + logStartOfTurn(game, activePlayer); checkTurnIsControlledByOtherPlayer(game, activePlayer.getId()); @@ -134,7 +143,7 @@ public class Turn implements Serializable { game.getPlayer(activePlayer.getId()).beginTurn(game); for (Phase phase : phases) { if (game.isPaused() || game.gameOver(null)) { - return; + return false; } if (!isEndTurnRequested() || phase.getType().equals(TurnPhase.END)) { currentPhase = phase; @@ -142,7 +151,7 @@ public class Turn implements Serializable { if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) { if (phase.play(game, activePlayer.getId())) { if (game.executingRollback()) { - return; + return false; } //20091005 - 500.4/703.4n game.emptyManaPools(); @@ -155,7 +164,7 @@ public class Turn implements Serializable { } } } - + return false; } public void resumePlay(Game game, boolean wasPaused) { @@ -327,4 +336,23 @@ public class Turn implements Serializable { return sb.toString(); } + private void logStartOfTurn(Game game, Player player) { + StringBuilder sb = new StringBuilder("Turn ").append(game.getState().getTurnNum()).append(" "); + sb.append(player.getLogName()); + sb.append(" ("); + int delimiter = game.getPlayers().size() - 1; + for (Player gamePlayer : game.getPlayers().values()) { + sb.append(gamePlayer.getLife()); + int poison = gamePlayer.getCounters().getCount(CounterType.POISON); + if (poison > 0) { + sb.append("[P:").append(poison).append("]"); + } + if (delimiter > 0) { + sb.append(" - "); + delimiter--; + } + } + sb.append(")"); + game.fireStatusEvent(sb.toString(), true); + } } From 29997ebf57cf116007f6530eb8adb2ff9f2e52eb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 17:23:09 +0200 Subject: [PATCH 18/18] * Added Eater of Days, Tariel, Reckoner of Souls, Island Sanctuary, Selvala, Explorer Returned and Awakening. --- .../sets/commander/TarielReckonerOfSouls.java | 119 +++++++++++++++++ .../conspiracy/SelvalaExplorerReturned.java | 52 ++++++++ .../src/mage/sets/darksteel/EaterOfDays.java | 69 ++++++++++ .../sets/fifthedition/IslandSanctuary.java | 52 ++++++++ .../sets/fourthedition/IslandSanctuary.java | 52 ++++++++ .../sets/limitedalpha/IslandSanctuary.java | 123 ++++++++++++++++++ .../sets/limitedbeta/IslandSanctuary.java | 52 ++++++++ .../masterseditioniv/IslandSanctuary.java | 52 ++++++++ .../sets/revisededition/IslandSanctuary.java | 52 ++++++++ .../src/mage/sets/stronghold/Awakening.java | 72 ++++++++++ .../unlimitededition/IslandSanctuary.java | 52 ++++++++ .../SelvalaExplorerReturned.java | 112 ++++++++++++++++ .../sets/zendikar/MagosiTheWaterveil.java | 62 ++------- .../dynamicvalue/common/ParleyCount.java | 98 ++++++++++++++ .../effects/common/UntapAllEffect.java | 69 ++++++++++ Mage/src/mage/constants/AbilityWord.java | 9 +- 16 files changed, 1044 insertions(+), 53 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/commander/TarielReckonerOfSouls.java create mode 100644 Mage.Sets/src/mage/sets/conspiracy/SelvalaExplorerReturned.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/EaterOfDays.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/limitedalpha/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/limitedbeta/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/revisededition/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/Awakening.java create mode 100644 Mage.Sets/src/mage/sets/unlimitededition/IslandSanctuary.java create mode 100644 Mage.Sets/src/mage/sets/vintagemasters/SelvalaExplorerReturned.java create mode 100644 Mage/src/mage/abilities/dynamicvalue/common/ParleyCount.java create mode 100644 Mage/src/mage/abilities/effects/common/UntapAllEffect.java diff --git a/Mage.Sets/src/mage/sets/commander/TarielReckonerOfSouls.java b/Mage.Sets/src/mage/sets/commander/TarielReckonerOfSouls.java new file mode 100644 index 00000000000..7cfe1e0b577 --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander/TarielReckonerOfSouls.java @@ -0,0 +1,119 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.commander; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author LevelX2 + */ +public class TarielReckonerOfSouls extends CardImpl { + + public TarielReckonerOfSouls(UUID ownerId) { + super(ownerId, 229, "Tariel, Reckoner of Souls", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{4}{W}{B}{R}"); + this.expansionSetCode = "CMD"; + this.supertype.add("Legendary"); + this.subtype.add("Angel"); + this.power = new MageInt(4); + this.toughness = new MageInt(7); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + // {T}: Choose a creature card at random from target opponent's graveyard. Put that card onto the battlefield under your control. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TarielReckonerOfSoulsEffect(), new TapSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + } + + public TarielReckonerOfSouls(final TarielReckonerOfSouls card) { + super(card); + } + + @Override + public TarielReckonerOfSouls copy() { + return new TarielReckonerOfSouls(this); + } +} + +class TarielReckonerOfSoulsEffect extends OneShotEffect { + + public TarielReckonerOfSoulsEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "Choose a creature card at random from target opponent's graveyard. Put that card onto the battlefield under your control"; + } + + public TarielReckonerOfSoulsEffect(final TarielReckonerOfSoulsEffect effect) { + super(effect); + } + + @Override + public TarielReckonerOfSoulsEffect copy() { + return new TarielReckonerOfSoulsEffect(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) { + Cards creatureCards = new CardsImpl(); + for (Card card : targetOpponent.getGraveyard().getCards(new FilterCreatureCard(), game)) { + creatureCards.add(card); + } + if (!creatureCards.isEmpty()) { + Card card = creatureCards.getRandom(game); + controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId()); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/conspiracy/SelvalaExplorerReturned.java b/Mage.Sets/src/mage/sets/conspiracy/SelvalaExplorerReturned.java new file mode 100644 index 00000000000..a455dc71609 --- /dev/null +++ b/Mage.Sets/src/mage/sets/conspiracy/SelvalaExplorerReturned.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.conspiracy; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class SelvalaExplorerReturned extends mage.sets.vintagemasters.SelvalaExplorerReturned { + + public SelvalaExplorerReturned(UUID ownerId) { + super(ownerId); + this.cardNumber = 51; + this.expansionSetCode = "CNS"; + } + + public SelvalaExplorerReturned(final SelvalaExplorerReturned card) { + super(card); + } + + @Override + public SelvalaExplorerReturned copy() { + return new SelvalaExplorerReturned(this); + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/EaterOfDays.java b/Mage.Sets/src/mage/sets/darksteel/EaterOfDays.java new file mode 100644 index 00000000000..9463deac87e --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/EaterOfDays.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.turn.SkipNextTurnSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LevelX2 + */ +public class EaterOfDays extends CardImpl { + + public EaterOfDays(UUID ownerId) { + super(ownerId, 120, "Eater of Days", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "DST"; + this.subtype.add("Leviathan"); + this.power = new MageInt(9); + this.toughness = new MageInt(8); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Trample + this.addAbility(TrampleAbility.getInstance()); + // When Eater of Days enters the battlefield, you skip your next two turns. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SkipNextTurnSourceEffect(2))); + } + + public EaterOfDays(final EaterOfDays card) { + super(card); + } + + @Override + public EaterOfDays copy() { + return new EaterOfDays(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/IslandSanctuary.java b/Mage.Sets/src/mage/sets/fifthedition/IslandSanctuary.java new file mode 100644 index 00000000000..096725df9c9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/IslandSanctuary.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 IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 315; + this.expansionSetCode = "5ED"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/IslandSanctuary.java b/Mage.Sets/src/mage/sets/fourthedition/IslandSanctuary.java new file mode 100644 index 00000000000..430788c2d02 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/IslandSanctuary.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.fourthedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 281; + this.expansionSetCode = "4ED"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/IslandSanctuary.java b/Mage.Sets/src/mage/sets/limitedalpha/IslandSanctuary.java new file mode 100644 index 00000000000..e6c8441f235 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/IslandSanctuary.java @@ -0,0 +1,123 @@ +/* + * 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.limitedalpha; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.IslandwalkAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends CardImpl { + + public IslandSanctuary(UUID ownerId) { + super(ownerId, 209, "Island Sanctuary", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "LEA"; + + // If you would draw a card during your draw step, instead you may skip that draw. If you do, until your next turn, you can't be attacked except by creatures with flying and/or islandwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IslandSanctuaryEffect())); + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} + +class IslandSanctuaryEffect extends ReplacementEffectImpl { + + private final static FilterCreaturePermanent notFlyingorIslandwalkCreatures = new FilterCreaturePermanent("except by creatures with flying and/or islandwalk"); + + static { + notFlyingorIslandwalkCreatures.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + notFlyingorIslandwalkCreatures.add(Predicates.not(new AbilityPredicate(IslandwalkAbility.class))); + } + + IslandSanctuaryEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would draw a card during your draw step, instead you may skip that draw. If you do, until your next turn, you can't be attacked except by creatures with flying and/or islandwalk"; + } + + IslandSanctuaryEffect(final IslandSanctuaryEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(event.getPlayerId()); + if (controller != null && controller.chooseUse(outcome, "Skip draw card? (If you do, until your next turn, you can't be attacked except by creatures with flying and/or islandwalk)", source, game)) { + game.informPlayers(controller.getLogName() + " skips his or her draw card action. Until his or her next turn, he or she can't be attacked except by creatures with flying and/or islandwalk"); + game.addEffect(new CantAttackYouAllEffect(Duration.UntilYourNextTurn, notFlyingorIslandwalkCreatures), source); + return true; + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getControllerId().equals(event.getPlayerId()) && game.getTurn().getStepType().equals(PhaseStep.DRAW); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public IslandSanctuaryEffect copy() { + return new IslandSanctuaryEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/IslandSanctuary.java b/Mage.Sets/src/mage/sets/limitedbeta/IslandSanctuary.java new file mode 100644 index 00000000000..3e66149dbb5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/IslandSanctuary.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.limitedbeta; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 211; + this.expansionSetCode = "LEB"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/IslandSanctuary.java b/Mage.Sets/src/mage/sets/masterseditioniv/IslandSanctuary.java new file mode 100644 index 00000000000..8dc77ce2315 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/IslandSanctuary.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.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 15; + this.expansionSetCode = "ME4"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/revisededition/IslandSanctuary.java b/Mage.Sets/src/mage/sets/revisededition/IslandSanctuary.java new file mode 100644 index 00000000000..a7e0b44651a --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/IslandSanctuary.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.revisededition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 209; + this.expansionSetCode = "3ED"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/Awakening.java b/Mage.Sets/src/mage/sets/stronghold/Awakening.java new file mode 100644 index 00000000000..51e8196e645 --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/Awakening.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.stronghold; + +import java.util.UUID; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.UntapAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author LevelX2 + */ +public class Awakening extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("creatures and lands"); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.LAND) + )); + } + + public Awakening(UUID ownerId) { + super(ownerId, 51, "Awakening", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); + this.expansionSetCode = "STH"; + + // At the beginning of each upkeep, untap all creatures and lands. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new UntapAllEffect(filter), TargetController.ANY, false)); + } + + public Awakening(final Awakening card) { + super(card); + } + + @Override + public Awakening copy() { + return new Awakening(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/IslandSanctuary.java b/Mage.Sets/src/mage/sets/unlimitededition/IslandSanctuary.java new file mode 100644 index 00000000000..f7c31dedb27 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/IslandSanctuary.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.unlimitededition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class IslandSanctuary extends mage.sets.limitedalpha.IslandSanctuary { + + public IslandSanctuary(UUID ownerId) { + super(ownerId); + this.cardNumber = 210; + this.expansionSetCode = "2ED"; + } + + public IslandSanctuary(final IslandSanctuary card) { + super(card); + } + + @Override + public IslandSanctuary copy() { + return new IslandSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/sets/vintagemasters/SelvalaExplorerReturned.java b/Mage.Sets/src/mage/sets/vintagemasters/SelvalaExplorerReturned.java new file mode 100644 index 00000000000..3a88cb54f45 --- /dev/null +++ b/Mage.Sets/src/mage/sets/vintagemasters/SelvalaExplorerReturned.java @@ -0,0 +1,112 @@ +/* + * 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.vintagemasters; + +import java.util.UUID; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.ParleyCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardAllEffect; +import mage.cards.CardImpl; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class SelvalaExplorerReturned extends CardImpl { + + public SelvalaExplorerReturned(UUID ownerId) { + super(ownerId, 260, "Selvala, Explorer Returned", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{G}{W}"); + this.expansionSetCode = "VMA"; + this.supertype.add("Legendary"); + this.subtype.add("Elf"); + this.subtype.add("Scout"); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Parley - {T}: Each player reveals the top card of his or her library. For each nonland card revealed this way, add {G} to your mana pool and you gain 1 life. Then each player draws a card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SelvalaExplorerReturnedEffect(), new TapSourceCost()); + ability.setAbilityWord(AbilityWord.PARLEY); + Effect effect = new DrawCardAllEffect(1); + effect.setText("Then each player draws a card"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public SelvalaExplorerReturned(final SelvalaExplorerReturned card) { + super(card); + } + + @Override + public SelvalaExplorerReturned copy() { + return new SelvalaExplorerReturned(this); + } +} + +class SelvalaExplorerReturnedEffect extends OneShotEffect { + + public SelvalaExplorerReturnedEffect() { + super(Outcome.Benefit); + this.staticText = "Each player reveals the top card of his or her library. For each nonland card revealed this way, add {G} to your mana pool and you gain 1 life"; + } + + public SelvalaExplorerReturnedEffect(final SelvalaExplorerReturnedEffect effect) { + super(effect); + } + + @Override + public SelvalaExplorerReturnedEffect copy() { + return new SelvalaExplorerReturnedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int parley = ParleyCount.getInstance().calculate(game, source, this); + if (parley > 0) { + controller.getManaPool().addMana(new Mana(0, parley, 0, 0, 0, 0, 0), game, source); + controller.gainLife(parley, game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/zendikar/MagosiTheWaterveil.java b/Mage.Sets/src/mage/sets/zendikar/MagosiTheWaterveil.java index 578345c2938..6850c94e1c8 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MagosiTheWaterveil.java +++ b/Mage.Sets/src/mage/sets/zendikar/MagosiTheWaterveil.java @@ -28,9 +28,6 @@ package mage.sets.zendikar; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -38,17 +35,15 @@ import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.ReturnToHandSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; +import mage.abilities.effects.common.turn.SkipNextTurnSourceEffect; import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; -import mage.constants.Outcome; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.turn.TurnMod; -import mage.players.Player; /** * @@ -62,21 +57,22 @@ public class MagosiTheWaterveil extends CardImpl { // Magosi, the Waterveil enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); - - // {tap}: Add {U} to your mana pool. + + // {T}: Add {U} to your mana pool. this.addAbility(new BlueManaAbility()); - - // {U}, {tap}: Put an eon counter on Magosi, the Waterveil. Skip your next turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MagosiTheWaterveilEffect(), new ManaCostsImpl("{U}")); + + // {U}, {T}: Put an eon counter on Magosi, the Waterveil. Skip your next turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.EON.createInstance()), new ManaCostsImpl("{U}")); + ability.addEffect(new SkipNextTurnSourceEffect()); ability.addCost(new TapSourceCost()); this.addAbility(ability); - - // {tap}, Remove an eon counter from Magosi, the Waterveil and return it to its owner's hand: Take an extra turn after this one. + + // {T}, Remove an eon counter from Magosi, the Waterveil and return it to its owner's hand: Take an extra turn after this one. Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddExtraTurnControllerEffect(), new TapSourceCost()); ability2.addCost(new RemoveCountersSourceCost(CounterType.EON.createInstance())); ability2.addCost(new ReturnToHandSourceCost()); this.addAbility(ability2); - + } public MagosiTheWaterveil(final MagosiTheWaterveil card) { @@ -88,35 +84,3 @@ public class MagosiTheWaterveil extends CardImpl { return new MagosiTheWaterveil(this); } } - -class MagosiTheWaterveilEffect extends OneShotEffect { - - public MagosiTheWaterveilEffect() { - super(Outcome.Neutral); - staticText = "Put an eon counter on Magosi, the Waterveil. Skip your next turn"; - } - - public MagosiTheWaterveilEffect(final MagosiTheWaterveilEffect effect) { - super(effect); - } - - @Override - public MagosiTheWaterveilEffect copy() { - return new MagosiTheWaterveilEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent magosi = game.getPermanent(source.getSourceId()); - Player player = game.getPlayer(source.getControllerId()); - - if (magosi != null) { - magosi.addCounters(CounterType.EON.createInstance(), game); - } - if (player != null) { - game.getState().getTurnMods().add(new TurnMod(player.getId(), true)); - } - return true; - } - -} \ No newline at end of file diff --git a/Mage/src/mage/abilities/dynamicvalue/common/ParleyCount.java b/Mage/src/mage/abilities/dynamicvalue/common/ParleyCount.java new file mode 100644 index 00000000000..008969e24fd --- /dev/null +++ b/Mage/src/mage/abilities/dynamicvalue/common/ParleyCount.java @@ -0,0 +1,98 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.dynamicvalue.common; + +import java.io.ObjectStreamException; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.MageSingleton; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.game.Game; +import mage.players.Player; + +/** + * Don't use this for continuous effects because it applies a reveal effect! + * + * @author LevelX2 + */ +public class ParleyCount implements DynamicValue, MageSingleton { + + private static final ParleyCount fINSTANCE = new ParleyCount(); + + private Object readResolve() throws ObjectStreamException { + return fINSTANCE; + } + + public static ParleyCount getInstance() { + return fINSTANCE; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + // Each player reveals the top card of his or her library. For each nonland card revealed this way + int parleyValue = 0; + MageObject sourceObject = game.getObject(sourceAbility.getSourceId()); + if (sourceObject != null) { + for (UUID playerId : game.getState().getPlayerList(sourceAbility.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player != null) { + Card card = player.getLibrary().getFromTop(game); + if (card != null) { + if (!card.getCardType().contains(CardType.LAND)) { + parleyValue++; + } + player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ")", new CardsImpl(card), game); + } + } + + } + } + return parleyValue; + } + + @Override + public DynamicValue copy() { + return fINSTANCE; + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "Parley"; + } + +} diff --git a/Mage/src/mage/abilities/effects/common/UntapAllEffect.java b/Mage/src/mage/abilities/effects/common/UntapAllEffect.java new file mode 100644 index 00000000000..b2b05b496d7 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/UntapAllEffect.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class UntapAllEffect extends OneShotEffect { + + private final FilterPermanent filter; + + public UntapAllEffect(FilterPermanent filter) { + super(Outcome.Untap); + staticText = "untap all " + filter.getMessage(); + this.filter = filter; + } + + public UntapAllEffect(final UntapAllEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + permanent.untap(game); + } + return true; + } + + @Override + public UntapAllEffect copy() { + return new UntapAllEffect(this); + } + +} diff --git a/Mage/src/mage/constants/AbilityWord.java b/Mage/src/mage/constants/AbilityWord.java index c050ab0e945..1ef899c62fd 100644 --- a/Mage/src/mage/constants/AbilityWord.java +++ b/Mage/src/mage/constants/AbilityWord.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.constants; /** @@ -33,15 +32,17 @@ package mage.constants; * @author LevelX2 */ public enum AbilityWord { + BLOODRUSH("Bloodrush"), CONSTELLATION("Constellation"), - FEROCIOUS("Ferocious"), + FEROCIOUS("Ferocious"), + FORMIDABLE("Formidable"), + GRANDEUR("Grandeur"), HELLBENT("Hellbent"), HEROIC("Heroic"), - FORMIDABLE("Formidable"), LANDFALL("Landfall"), METALCRAFT("Metalcraft"), - GRANDEUR("Grandeur"), + PARLEY("Parley"), RAID("Raid"); private final String text;