From ece8248f11edbee2eeac8b5309287dd30ac83f96 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sat, 25 Jul 2015 11:34:04 +0300 Subject: [PATCH 01/34] 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/34] 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/34] 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 c1dc053e9f958a5fad661f011c2691ee8000d7d6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 00:01:30 +0200 Subject: [PATCH 04/34] * Artificer's Epiphany - Fixed tooltip text (fixes #1148). --- Mage.Sets/src/mage/sets/magicorigins/ArtificersEpiphany.java | 4 ++-- .../effects/common/discard/DiscardControllerEffect.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magicorigins/ArtificersEpiphany.java b/Mage.Sets/src/mage/sets/magicorigins/ArtificersEpiphany.java index dc5b68d353f..cab1d89f252 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/ArtificersEpiphany.java +++ b/Mage.Sets/src/mage/sets/magicorigins/ArtificersEpiphany.java @@ -44,8 +44,8 @@ import mage.filter.predicate.mageobject.CardTypePredicate; * @author fireshoes */ public class ArtificersEpiphany extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifacts"); + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("you control no artifacts"); static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); diff --git a/Mage/src/mage/abilities/effects/common/discard/DiscardControllerEffect.java b/Mage/src/mage/abilities/effects/common/discard/DiscardControllerEffect.java index da0da5f3cba..fa9d314a213 100644 --- a/Mage/src/mage/abilities/effects/common/discard/DiscardControllerEffect.java +++ b/Mage/src/mage/abilities/effects/common/discard/DiscardControllerEffect.java @@ -27,12 +27,12 @@ */ package mage.abilities.effects.common.discard; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -98,7 +98,7 @@ public class DiscardControllerEffect extends OneShotEffect { } private void setText() { - StringBuilder sb = new StringBuilder("Discard "); + StringBuilder sb = new StringBuilder("discard "); if (amount.toString().equals("1")) { sb.append("a"); } else { From 9dd6e616cdb40254bb6b7470be6e5d4c7d737303 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 00:38:56 +0200 Subject: [PATCH 05/34] * Fixed that commander mana replacement effect did wrong mana replacements if additional mana was produced from abilities of opponents (fixes #1146). --- .../duel/CommanderManaReplacmentTest.java | 82 +++++++++++++ Mage/src/mage/game/events/GameEvent.java | 15 ++- Mage/src/mage/players/ManaPool.java | 108 +++++++++--------- 3 files changed, 149 insertions(+), 56 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderManaReplacmentTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderManaReplacmentTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderManaReplacmentTest.java new file mode 100644 index 00000000000..84ee71449bd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderManaReplacmentTest.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 org.mage.test.commander.duel; + +import java.io.FileNotFoundException; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ +public class CommanderManaReplacmentTest extends CardTestCommanderDuelBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + setDecknamePlayerA("CommanderDuel_UW.dck"); // Commander = Daxos of Meletis + return super.createNewGameAndPlayers(); + } + + /** + * This issue appears to arise in both Commander Two Player Duel and + * Commander Free For All. Whenever a player (call her player 1) controls a + * permanent with a mana doubling continuous effect (e.g. Mana Flare) and + * another player (call her player 2) taps an affected permanent for mana + * that is outside of player 1's color identity, player 2 gets an additional + * colorless mana rather than the correct color of mana. I suspect the + * reason for this is that Xmage is treating Mana Flare as producing mana of + * the appropriate color and adding it to another player's mana pool, which + * is both incorrect in the game rules and would cause this problem. + * + * For example, I am playing a mono red deck and control a Mana Flare. You + * are playing a mono green deck and you tap a Forest for mana. You should + * add GG to your mana pool, but instead, Xmage shows you adding 1G to your + * mana pool. + */ + @Test + public void castCommanderWithFlash() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.HAND, playerA, "Vedalken Mastermind", 1); // {U}{U} + + addCard(Zone.BATTLEFIELD, playerB, "Mana Flare"); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}"); // should add {U}{U} + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind"); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Vedalken Mastermind", 1); + + } + +} diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 433ad158c39..fcabb52c61a 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -119,7 +119,20 @@ public class GameEvent implements Serializable { */ SPELL_CAST, ACTIVATE_ABILITY, ACTIVATED_ABILITY, - ADD_MANA, MANA_ADDED, + /* ADD_MANA + targetId id of the ability that added the mana + sourceId sourceId of the ability that added the mana + playerId player the mana is added to the mana pool for + mana the mana added + */ + ADD_MANA, + /* MANA_ADDED + targetId id of the ability that added the mana + sourceId sourceId of the ability that added the mana + playerId player the mana is added to the mana pool for + mana the mana added + */ + MANA_ADDED, /* MANA_PAYED targetId id if the ability the mana was paid for (not the sourceId) sourceId sourceId of the mana source diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 200faae6b79..f4745ac20ca 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -1,31 +1,30 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.players; import java.io.Serializable; @@ -63,7 +62,7 @@ public class ManaPool implements Serializable { private boolean autoPayment; // auto payment from mana pool: true - mode is active private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool private ManaType unlockedManaType; // type of mana that was selected to pay manually - + private final Set doNotEmptyManaTypes = new HashSet<>(); public ManaPool(UUID playerId) { @@ -75,7 +74,7 @@ public class ManaPool implements Serializable { public ManaPool(final ManaPool pool) { this.playerId = pool.playerId; - for (ManaPoolItem item: pool.manaItems) { + for (ManaPoolItem item : pool.manaItems) { manaItems.add(item.copy()); } this.autoPayment = pool.autoPayment; @@ -105,24 +104,24 @@ public class ManaPool implements Serializable { } /** - * + * * @param manaType the mana type that should be paid * @param ability * @param filter * @param game - * @return + * @return */ public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game) { if (!autoPayment && !manaType.equals(unlockedManaType)) { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } - if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) { + if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) { // if automatic restricted payment and there is laready mana in the pool // and the needed mana type was not unlocked, nothing will be paid return false; } - + if (getConditional(manaType, ability, filter, game) > 0) { removeConditional(manaType, ability, game); lockManaType(); // pay only one mana if mana payment is set to manually @@ -134,7 +133,7 @@ public class ManaPool implements Serializable { if (!filter.match(sourceObject, game)) { continue; } - } + } if (!manaType.equals(unlockedManaType) && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { // no mana added beyond the stock so don't auto pay this continue; @@ -202,7 +201,7 @@ public class ManaPool implements Serializable { public void clearEmptyManaPoolRules() { doNotEmptyManaTypes.clear(); } - + public void addDoNotEmptyManaType(ManaType manaType) { doNotEmptyManaTypes.add(manaType); } @@ -217,7 +216,7 @@ public class ManaPool implements Serializable { if (!doNotEmptyManaTypes.contains(manaType)) { if (item.get(manaType) > 0) { if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) { - if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { + if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { int amount = item.get(manaType); item.clear(manaType); item.add(ManaType.COLORLESS, amount); @@ -230,7 +229,7 @@ public class ManaPool implements Serializable { if (conditionalItem != null) { if (conditionalItem.get(manaType) > 0) { if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) { - if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { + if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { int amount = conditionalItem.get(manaType); conditionalItem.clear(manaType); conditionalItem.add(ManaType.COLORLESS, amount); @@ -246,8 +245,8 @@ public class ManaPool implements Serializable { if (item.count() == 0) { it.remove(); } - } - return total; + } + return total; } private int payX(Ability ability, Game game) { @@ -261,8 +260,7 @@ public class ManaPool implements Serializable { total += item.count(); it.remove(); } - } - else { + } else { total += item.count(); it.remove(); } @@ -272,10 +270,11 @@ public class ManaPool implements Serializable { /** * remove all mana from pool that applies and that matches filter + * * @param ability * @param game * @param filter - * @return + * @return */ public int payX(Ability ability, Game game, FilterMana filter) { if (filter == null) { @@ -297,8 +296,7 @@ public class ManaPool implements Serializable { } } } - } - else { + } else { if (filter.isBlack()) { total += item.getBlack(); item.removeBlack(); @@ -333,7 +331,7 @@ public class ManaPool implements Serializable { public Mana getMana() { Mana m = new Mana(); - for (ManaPoolItem item: manaItems) { + for (ManaPoolItem item : manaItems) { m.add(item.getMana()); } return m; @@ -375,12 +373,12 @@ public class ManaPool implements Serializable { public void addMana(Mana manaToAdd, Game game, Ability source) { addMana(manaToAdd, game, source, false); } - + public void addMana(Mana manaToAdd, Game game, Ability source, boolean emptyOnTurnsEnd) { Mana mana = manaToAdd.copy(); - if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), source.getControllerId(), mana))) { + if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) { if (mana instanceof ConditionalMana) { - ManaPoolItem item = new ManaPoolItem((ConditionalMana)mana, source.getSourceId(), source.getOriginalId()); + ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceId(), source.getOriginalId()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); } @@ -389,18 +387,18 @@ public class ManaPool implements Serializable { ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getColorless(), source.getSourceId(), source.getOriginalId(), mana.getFlag()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); - } + } this.manaItems.add(item); } - GameEvent event = GameEvent.getEvent(GameEvent.EventType.MANA_ADDED, source.getId(), source.getSourceId(), source.getControllerId()); - event.setData(mana.toString()); - game.fireEvent(event); + ManaEvent manaEvent = new ManaEvent(EventType.MANA_ADDED, source.getId(), source.getSourceId(), playerId, mana); + manaEvent.setData(mana.toString()); + game.fireEvent(manaEvent); } } public List getConditionalMana() { List conditionalMana = new ArrayList<>(); - for (ManaPoolItem item: manaItems) { + for (ManaPoolItem item : manaItems) { if (item.isConditional()) { conditionalMana.add(item.getConditionalMana()); } @@ -410,7 +408,7 @@ public class ManaPool implements Serializable { public int count() { int x = 0; - for (ManaPoolItem item: manaItems) { + for (ManaPoolItem item : manaItems) { x += item.count(); } return x; @@ -465,7 +463,7 @@ public class ManaPool implements Serializable { mana.setStock(mana.count()); } } - + private boolean wasManaAddedBeyondStock() { for (ManaPoolItem mana : manaItems) { if (mana.getStock() < mana.count()) { From 25db567c8988c8fddc8223216ad8776075145578 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 08:59:43 +0200 Subject: [PATCH 06/34] * Sekki, Seasons' Guide - Fixed that it wrongly returned to hand instead to battlefield. --- .../src/mage/sets/saviorsofkamigawa/SekkiSeasonsGuide.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/SekkiSeasonsGuide.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/SekkiSeasonsGuide.java index 649c2a88c63..0d45c155313 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/SekkiSeasonsGuide.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/SekkiSeasonsGuide.java @@ -36,7 +36,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.constants.CardType; @@ -82,7 +82,7 @@ public class SekkiSeasonsGuide extends CardImpl { // Sacrifice eight Spirits: Return Sekki from your graveyard to the battlefield. this.addAbility(new SimpleActivatedAbility( Zone.GRAVEYARD, - new ReturnSourceFromGraveyardToHandEffect(), + new ReturnSourceFromGraveyardToBattlefieldEffect(), new SacrificeTargetCost(new TargetControlledPermanent(8, 8, filter, true)))); } From 768fd17d931db5483adb2d1deea11deb162a0782 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 09:19:46 +0200 Subject: [PATCH 07/34] * Some spelling changes. --- .../java/mage/client/game/PlayAreaPanel.java | 2 +- .../client/game/PlayAreaPanelOptions.java | 63 ++++++++++--------- Mage/src/mage/game/GameImpl.java | 2 +- Mage/src/mage/game/GameOptions.java | 12 ++-- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 101df9709ad..a3d2f246c4d 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -333,7 +333,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }; - JMenu rollbackMainItem = new JMenu("Roll back"); + JMenu rollbackMainItem = new JMenu("Rollback"); rollbackMainItem.setMnemonic(KeyEvent.VK_R); rollbackMainItem.setToolTipText("The game will be rolled back to the start of the requested turn if all players agree."); popupMenu.add(rollbackMainItem); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java index 377b7591c4b..fb6cfe194aa 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java @@ -1,30 +1,30 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.client.game; /** @@ -39,20 +39,21 @@ public class PlayAreaPanelOptions { this.playerItself = playerItself; this.rollbackTurnsAllowed = rollbackTurnsAllowed; } - + /** - * true if the client is a player / false if the client is a watcher + * true if the client is a player / false if the client is a watcher */ public boolean isPlayer = false; /** - * true if the player is the client player itself, false if the player is another player playing with the clinet player + * true if the player is the client player itself, false if the player is + * another player playing with the clinet player */ public boolean playerItself = false; /** - * true if the player can roll back turns if all players agree + * true if the player can rollback turns if all players agree */ public boolean rollbackTurnsAllowed = false; - + } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index b14cd2908a0..858b5b8fff0 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -174,7 +174,7 @@ public abstract class GameImpl implements Game, Serializable { protected GameState state; private transient Stack savedStates = new Stack<>(); protected transient GameStates gameStates = new GameStates(); - // game states to allow player roll back + // game states to allow player rollback protected transient Map gameStatesRollBack = new HashMap<>(); protected boolean executingRollback; diff --git a/Mage/src/mage/game/GameOptions.java b/Mage/src/mage/game/GameOptions.java index 7767880a589..fc3c8a7424f 100644 --- a/Mage/src/mage/game/GameOptions.java +++ b/Mage/src/mage/game/GameOptions.java @@ -4,8 +4,8 @@ import java.io.Serializable; import mage.constants.PhaseStep; /** - * Game options for Mage game. - * Mainly used in tests to configure {@link GameImpl} with specific params. + * Game options for Mage game. Mainly used in tests to configure + * {@link GameImpl} with specific params. * * @author ayratn */ @@ -23,8 +23,8 @@ public class GameOptions implements Serializable { public boolean testMode = false; /** - * Defines the turn number game should stop on. - * By default, is null meaning that game shouldn't stop on any specific turn. + * Defines the turn number game should stop on. By default, is null meaning + * that game shouldn't stop on any specific turn. */ public Integer stopOnTurn = null; @@ -37,9 +37,9 @@ public class GameOptions implements Serializable { * If true, library won't be shuffled at the beginning of the game */ public boolean skipInitShuffling = false; - + /** - * If true, players can roll back turn if all players agree + * If true, players can rollback turn if all players agree */ public boolean rollbackTurnsAllowed = true; } From 5694f8aa8c88485be8102fecc5a2bb2054f76388 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 09:19:57 +0200 Subject: [PATCH 08/34] * Added a test. --- .../AI/basic/PreventRepeatedActionsTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/PreventRepeatedActionsTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/PreventRepeatedActionsTest.java index 56b924d8e9b..7237a3fc9b8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/PreventRepeatedActionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/PreventRepeatedActionsTest.java @@ -66,4 +66,21 @@ public class PreventRepeatedActionsTest extends CardTestPlayerBaseAI { Assert.assertEquals("AI should only used Equipment once", 2, tappedLands); } + /** + * If the AI on a local server gets control of a Basalt Monolith it will + * infinite loop taping for three mana and then using the mana to untap lol. + * Seeing the computer durdle troll is quite the hillarious thing + */ + @Test + public void testBasaltMonolith() { + // Basalt Monolith doesn't untap during your untap step. + // {T}: Add {3} to your mana pool. + // {3}: Untap Basalt Monolith. + addCard(Zone.BATTLEFIELD, playerA, "Basalt Monolith", 1); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped("Basalt Monolith", false); + } } From b029de3ec9dbd2af034aa5aaf8a8393f27e47644 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 26 Jul 2015 10:17:04 +0200 Subject: [PATCH 09/34] * Added id and colored font to shown related object names of stack abilities. --- Mage.Common/src/mage/view/CardsView.java | 5 +-- .../src/mage/view/StackAbilityView.java | 3 +- Mage/src/mage/util/GameLog.java | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Mage.Common/src/mage/view/CardsView.java b/Mage.Common/src/mage/view/CardsView.java index a4400c5faad..0107ee0ee03 100644 --- a/Mage.Common/src/mage/view/CardsView.java +++ b/Mage.Common/src/mage/view/CardsView.java @@ -49,6 +49,7 @@ import mage.game.GameState; import mage.game.command.Emblem; import mage.game.permanent.Permanent; import mage.target.targetpointer.TargetPointer; +import mage.util.GameLog; /** * @@ -145,11 +146,11 @@ public class CardsView extends LinkedHashMap { for (UUID uuid : abilityTargets) { MageObject mageObject = game.getObject(uuid); if (mageObject != null) { - names.add(mageObject.getName()); + names.add(GameLog.getColoredObjectIdNameForTooltip(mageObject)); } } if (!names.isEmpty()) { - abilityView.getRules().add("Related to: " + names.toString() + ""); + abilityView.getRules().add("Related objects: " + names.toString() + ""); } } } diff --git a/Mage.Common/src/mage/view/StackAbilityView.java b/Mage.Common/src/mage/view/StackAbilityView.java index a55b78c9c8f..6fb4d3c4863 100644 --- a/Mage.Common/src/mage/view/StackAbilityView.java +++ b/Mage.Common/src/mage/view/StackAbilityView.java @@ -41,6 +41,7 @@ import mage.game.Game; import mage.game.stack.StackAbility; import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.TargetPointer; +import mage.util.GameLog; /** * @@ -118,7 +119,7 @@ public class StackAbilityView extends CardView { if ((mageObject instanceof Card) && ((Card) mageObject).isFaceDown(game)) { continue; } - names.add(mageObject.getName()); + names.add(GameLog.getColoredObjectIdNameForTooltip(mageObject)); } } diff --git a/Mage/src/mage/util/GameLog.java b/Mage/src/mage/util/GameLog.java index 544b0a2d359..b6a59464e67 100644 --- a/Mage/src/mage/util/GameLog.java +++ b/Mage/src/mage/util/GameLog.java @@ -39,6 +39,7 @@ public class GameLog { static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate + // colors for more dark background static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen static final String LOG_COLOR_RED = "#FF6347"; // Tomato static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue @@ -46,6 +47,14 @@ public class GameLog { static final String LOG_COLOR_WHITE = "#F0E68C"; // Khaki static final String LOG_COLOR_MULTI = "#DAA520"; // GoldenRod static final String LOG_COLOR_COLORLESS = "#B0C4DE"; // LightSteelBlue + // colors for tooltip (light background) + static final String LOG_TT_COLOR_RED = "Red"; // Tomato + static final String LOG_TT_COLOR_GREEN = "Green"; // LightGreen + static final String LOG_TT_COLOR_BLUE = "Blue"; + static final String LOG_TT_COLOR_BLACK = "Black"; + static final String LOG_TT_COLOR_WHITE = "#CCDB00"; + static final String LOG_TT_COLOR_MULTI = "#FFAC40"; + static final String LOG_TT_COLOR_COLORLESS = "#94A4BA"; static final String LOG_COLOR_NEUTRAL = "#F0F8FF"; // AliceBlue public static String replaceNameByColoredName(MageObject mageObject, String text) { @@ -60,6 +69,10 @@ public class GameLog { return "" + mageObject.getName() + " [" + mageObject.getId().toString().substring(0, 3) + "]"; } + public static String getColoredObjectIdNameForTooltip(MageObject mageObject) { + return "" + mageObject.getName() + " [" + mageObject.getId().toString().substring(0, 3) + "]"; + } + public static String getNeutralColoredText(String text) { return "" + text + ""; } @@ -97,4 +110,22 @@ public class GameLog { return LOG_COLOR_BLACK; } } + + private static String getTooltipColorName(ObjectColor objectColor) { + if (objectColor.isMulticolored()) { + return LOG_TT_COLOR_MULTI; + } else if (objectColor.isColorless()) { + return LOG_TT_COLOR_COLORLESS; + } else if (objectColor.isRed()) { + return LOG_TT_COLOR_RED; + } else if (objectColor.isGreen()) { + return LOG_TT_COLOR_GREEN; + } else if (objectColor.isBlue()) { + return LOG_TT_COLOR_BLUE; + } else if (objectColor.isWhite()) { + return LOG_TT_COLOR_WHITE; + } else { + return LOG_TT_COLOR_BLACK; + } + } } From 77e53d6bbd01f3558f9f8ed563b56acd832f726c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Sun, 26 Jul 2015 11:49:31 +0300 Subject: [PATCH 10/34] 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 11/34] 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 12/34] 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 84d23538196a362154a50dc96218b5e5b3365955 Mon Sep 17 00:00:00 2001 From: Simown Date: Sun, 26 Jul 2015 12:45:14 +0100 Subject: [PATCH 13/34] Fix for Random booster draft iterates endless while booster generation (#1136) --- Mage.Sets/src/mage/sets/DragonsMaze.java | 3 ++- Mage.Sets/src/mage/sets/FateReforged.java | 5 ++--- Mage/src/mage/cards/ExpansionSet.java | 14 ++++---------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/sets/DragonsMaze.java b/Mage.Sets/src/mage/sets/DragonsMaze.java index 45c316b308e..1fe91979797 100644 --- a/Mage.Sets/src/mage/sets/DragonsMaze.java +++ b/Mage.Sets/src/mage/sets/DragonsMaze.java @@ -84,7 +84,8 @@ public class DragonsMaze extends ExpansionSet { savedCardsInfos = CardRepository.instance.findCards(criteria); savedCards.put(rarity, savedCardsInfos); } - return savedCardsInfos; + // Return a copy of the saved cards information, as not to modify the original. + return new ArrayList<>(savedCardsInfos); } else { return super.getCardsByRarity(rarity); } diff --git a/Mage.Sets/src/mage/sets/FateReforged.java b/Mage.Sets/src/mage/sets/FateReforged.java index f1126deeb10..76d31b292a2 100644 --- a/Mage.Sets/src/mage/sets/FateReforged.java +++ b/Mage.Sets/src/mage/sets/FateReforged.java @@ -81,9 +81,8 @@ public class FateReforged extends ExpansionSet { savedCardsInfos = CardRepository.instance.findCards(criteria); savedCards.put(rarity, savedCardsInfos); } - List cards = new ArrayList<>(); - cards.addAll(savedCardsInfos); - return cards; + // Return a copy of the saved cards information, as not to modify the original. + return new ArrayList<>(savedCardsInfos); } else { return super.getCardsByRarity(rarity); } diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java index 5c307480558..23c5649fbe3 100644 --- a/Mage/src/mage/cards/ExpansionSet.java +++ b/Mage/src/mage/cards/ExpansionSet.java @@ -117,16 +117,11 @@ public abstract class ExpansionSet implements Serializable { // if the packs are too big, it removes the first cards. // since it adds lands then commons before uncommons // and rares this should be the least disruptive. - List theBooster = this.createBooster(); List commons = getCardsByRarity(Rarity.COMMON); - int iterations = 0; - while (15 > theBooster.size() && !commons.isEmpty()) { + + while (15 > theBooster.size()) { addToBooster(theBooster, commons); - iterations++; - if (iterations > 14) { - break; - } } while (theBooster.size() > 15) { @@ -301,9 +296,8 @@ public abstract class ExpansionSet implements Serializable { savedCardsInfos = CardRepository.instance.findCards(criteria); savedCards.put(rarity, savedCardsInfos); } - List cards = new ArrayList<>(); - cards.addAll(savedCardsInfos); - return cards; + // Return a copy of the saved cards information, as not to modify the original. + return new ArrayList<>(savedCardsInfos); } public List getSpecialCommon() { From 2d5402b8e87ef827a327f61d13d7e13e3e5c020e Mon Sep 17 00:00:00 2001 From: benjamin Date: Sun, 26 Jul 2015 20:00:44 -0400 Subject: [PATCH 14/34] Added a Historical Standard deck validator, conforming to most of the rules found at http://thattournament.website/historic-tournament.php --- .../src/mage/deck/AllStandards.java | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java new file mode 100644 index 00000000000..009c89c6da2 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java @@ -0,0 +1,253 @@ +package mage.deck; + +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.cards.decks.Constructed; +import mage.cards.decks.Deck; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +/** + * This class represents a deck conforming to the rules contained in the + * subreddit /r/AllStandards. + * + * This class was originally made to work with the historical standard ruleset. + * Data taken from http://thattournament.website/historic-tournament.php + * (site changed, originally thtp://mtgt.nfshost.com/historic-tournament.php) + * + * If there are any questions or corrections, feel free to contact me. + * + * @author Marthinwurer (at gmail.com) + */ +public class AllStandards extends Constructed { + + /* + * This array stores the set codes of each standard up to + * Kamigawa/Ravnica standard, where rotation stabilized. + */ + protected static final String[][] standards = { + + // the first 10 standards are commented out to adhere to the all + // standards ruleset, instaed of the historical standard ruleset. + +// // 1st standard: The Dark, Fallen Empires, and 4th. +// { "DRK", "FEM", "4ED" }, +// +// // 2nd standard: 4th, Fallen Empires, Ice Age, Chronicles, Homelands, +// // Alliances, and Mirage. +// {"FEM", "4ED", "ICE", "HML", "ALL", "MIR"}, +// +// // 3rd standard: 4th, Chronicles, Alliances, Mirage, Visions. +// {"4ED", "ALL", "MIR", "VIS"}, +// +// // 4th Standard: Ice Age, Homelands, Alliances, Mirage, Visions, 5th, +// // and Weatherlight. +// {"ICE", "HML", "ALL", "MIR", "VIS", "5ED", "WTH"}, +// +// // 5th Standard: Mirage, Visions, 5th, Weatherlight, Tempest, +// // Stronghold, and Exodus. +// {"MIR", "VIS", "5ED", "WTH", "TMP", "STH", "EXO"}, +// +// // 6th Standard: 5th, Tempest, Stronghold, Exodus, Urza's Saga, Urza's +// // Legacy, Urza's Destiny. +// {"5ED", "TMP", "STH", "EXO", "USG", "ULG"}, +// +// // 7th Standard: Tempest, Stronghold, Exodus, Urza's Saga, Urza's +// // Legacy, 6th, Urza's Destiny. +// {"TMP", "STH", "EXO", "USG", "ULG", "6ED", "UDS"}, +// +// // 8th Standard: Urza's Saga, Urza's Legacy, 6th, Urza's Destiny, +// // Mercadian Masques, Nemesis, Prophecy. +// {"USG", "ULG", "6ED", "UDS", "MMQ", "NMS", "PCY"}, +// +// // 9th Standard +// {"6ED", "MMQ", "NMS", "PCY", "INV", "PLS"}, +// +// // 10th Standard +// {"7ED", "MMQ", "NMS", "PCY", "INV", "PLS", "APC"}, + + // 11th Standard + {"7ED", "INV", "APC", "PLS", "ODY", "TOR", "JUD"}, + + // 12th Standard + {"7ED", "ODY", "TOR", "JUD", "ONS", "LGN", "LGN"}, + + // 13th Standard + {"8ED", "ODY", "TOR", "JUD", "ONS", "LGN", "LGN"}, + + // 14th Standard + {"8ED", "ONS", "LGN", "LGN", "MRD", "DST", "5DN"}, + + // 15th Standard + {"8ED", "MRD", "DST", "5DN", "CHK", "BOK", "SOK"}, + + // 16th Standard + {"9ED", "MRD", "DST", "5DN", "CHK", "BOK", "SOK"}, + + // 17th Standard + {"9ED", "CHK", "BOK", "SOK", "RAV", "GPT", "DIS", "CSP"}, + + // 18th Standard + {"9ED", "RAV", "GPT", "DIS", "CSP", "TSP", "TSB", "PLC", "FUT"}, + + // 19th Standard + {"10E", "RAV", "GPT", "DIS", "CSP", "TSP", "PLC", "FUT"}, + + // 20th Standard + {"10E", "CSP", "TSP", "PLC", "FUT", "LRW", "MOR", "SHM", "EVE"}, + + // 21st Standard + {"10E", "LRW", "MOR", "SHM", "EVE", "ALA", "CON", "ARB"} + }; + + /** + * Constructor. Don't need to mess with any of the sets yet; that will be + * done in the overridden validate function. + */ + public AllStandards() { + super("Constructed - All Standards"); + + // banned cards for historical standard ruleset commented out. + +// // banned cards +// banned.add("Balance"); +// // Not banned in the format, but it is either this or Misty, and most +// // people choose Misty. +// banned.add("Batterskull"); +// banned.add("Memory Jar"); +// banned.add("Mind Over Matter"); +// banned.add("Mind Twist"); +// banned.add("Skullclamp"); +// banned.add("Tolarian Academy"); +// banned.add("Yawgmoth's Bargain"); + + banned.add("Ancient Den"); + banned.add("Arcbound Ravager"); + banned.add("Disciple of the Vault"); + banned.add("Great Furnace"); + banned.add("Jace, the Mind Sculptor"); + banned.add("Seat of the Synod"); + banned.add("Skullclamp"); + banned.add("Tree of Tales"); + banned.add("Vault of Whispers"); + } + + /** + * Overridden validate function. Changes the standard sets, then uses the + * regular validation function to test validity. + * @param deck - the deck to validate. + * @return + */ + @Override + public boolean validate(Deck deck) { + + Map leastInvalid = null; + + boolean valid = false; + + // up to Lorwyn/Alara, standards will have to be hard-coded. + // iterate through the array of standards. + for (String[] sets : standards) { + + // clear the invalid list + invalid.clear(); + + // add the sets to the setCodes. + setCodes = new ArrayList<>(Arrays.asList(sets)); + + // validate it. If it validates, clear the invalid cards and break. + if (super.validate(deck)) { + valid = true; + break; + } + + // if the map holding the invalid cards is empty, set it to a + // copy of the current invalid list. + if (leastInvalid == null) { + leastInvalid = new HashMap<>(this.getInvalid()); + continue; + } + + // see how many invalid cards there are. if there are less invalid + // cards than the stored invalid list, assign the current invalid + // to leastInvalid. + if (leastInvalid.size() > this.getInvalid().size()) { + leastInvalid = new HashMap<>(this.getInvalid()); + } + } + + // After testing the first few standards, do the regular ones. + // set the initial starting and ending date, as well as the current. + GregorianCalendar start = new GregorianCalendar(2006, + Calendar.SEPTEMBER, 1); + GregorianCalendar end = new GregorianCalendar(2008, + Calendar.SEPTEMBER, 1); + GregorianCalendar current = new GregorianCalendar(); + + // use the method for determining regular standard legality, but change + // the date for each standard. + while (end.before(current) && !valid) { + + // clear the invalid list and set codes. + setCodes.clear(); + invalid.clear(); + + // increment the start and end dates. + start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1); + end.set(Calendar.YEAR, start.get(Calendar.YEAR) + 2); + + // Get the sets in that time period. + // (code taken from standard.java) + for (ExpansionSet set : Sets.getInstance().values()) { + if (set.getReleaseDate().after(start.getTime()) + && set.getReleaseDate().before(end.getTime()) + && (set.getSetType() == SetType.CORE || set.getSetType() == SetType.EXPANSION)) { + setCodes.add(set.getCode()); + } + } + + // if either of the mirrodin blocks are in the time period, ban + // misty and darksteel citadel + if( setCodes.contains("MRD") || setCodes.contains("SOM")){ + banned.add("Darksteel Citadel"); + banned.add("Stoneforge Mystic"); + }else{ + banned.remove("Darksteel Citadel"); + banned.remove("Stoneforge Mystic"); + } + + // validate it. If it validates, clear the invalid cards and break. + if (super.validate(deck)) { + invalid.clear(); + valid = true; + break; + } + + // see how many invalid cards there are. if there are less invalid + // cards than the stored invalid list, assign the current invalid + // to leastInvalid. + if (leastInvalid == null){ + leastInvalid = new HashMap<>(this.getInvalid()); + + }else if (leastInvalid.size() > this.getInvalid().size()) { + leastInvalid = new HashMap<>(this.getInvalid()); + } + } + + // if no standard environment is valid, set the invalid to the + // invalid that had the least errors. + if( !valid ){ + this.invalid = new HashMap<>(leastInvalid); + } + + // return the validity. + return valid; + } + +} From bd5d13a32362dc7631b05216bd6f82d6a4f7da2d Mon Sep 17 00:00:00 2001 From: benjamin Date: Sun, 26 Jul 2015 22:59:18 -0400 Subject: [PATCH 15/34] Updated Historical Standard validators changed name of AllStandards.java to more accurately reflect the format; it uses the new date range and banlist from /r/AllStandards. Historical Standard uses the banlist from http://thattournament.website/historic-tournament.php --- .../src/mage/deck/HistoricalStandard.java | 227 ++++++++++++++++++ .../{AllStandards.java => SuperStandard.java} | 70 ++---- 2 files changed, 241 insertions(+), 56 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java rename Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/{AllStandards.java => SuperStandard.java} (73%) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java new file mode 100644 index 00000000000..1c8ba55341f --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java @@ -0,0 +1,227 @@ +package mage.deck; + +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.cards.decks.Constructed; +import mage.cards.decks.Deck; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +/** + * This class represents a deck from any past standard. + * + * This class was originally made to work with the historical standard ruleset. + * Data taken from http://thattournament.website/historic-tournament.php + * (site changed, originally thtp://mtgt.nfshost.com/historic-tournament.php) + * + * If there are any questions or corrections, feel free to contact me. + * + * @author Marthinwurer (at gmail.com) + */ +public class HistoricalStandard extends Constructed { + + /* + * This array stores the set codes of each standard up to + * Kamigawa/Ravnica standard, where rotation stabilized. + */ + protected static final String[][] standards = { + + // 1st standard: The Dark, Fallen Empires, and 4th. + { "DRK", "FEM", "4ED" }, + + // 2nd standard: 4th, Fallen Empires, Ice Age, Chronicles, Homelands, + // Alliances, and Mirage. + {"FEM", "4ED", "ICE", "HML", "ALL", "MIR"}, + + // 3rd standard: 4th, Chronicles, Alliances, Mirage, Visions. + {"4ED", "ALL", "MIR", "VIS"}, + + // 4th Standard: Ice Age, Homelands, Alliances, Mirage, Visions, 5th, + // and Weatherlight. + {"ICE", "HML", "ALL", "MIR", "VIS", "5ED", "WTH"}, + + // 5th Standard: Mirage, Visions, 5th, Weatherlight, Tempest, + // Stronghold, and Exodus. + {"MIR", "VIS", "5ED", "WTH", "TMP", "STH", "EXO"}, + + // 6th Standard: 5th, Tempest, Stronghold, Exodus, Urza's Saga, Urza's + // Legacy, Urza's Destiny. + {"5ED", "TMP", "STH", "EXO", "USG", "ULG"}, + + // 7th Standard: Tempest, Stronghold, Exodus, Urza's Saga, Urza's + // Legacy, 6th, Urza's Destiny. + {"TMP", "STH", "EXO", "USG", "ULG", "6ED", "UDS"}, + + // 8th Standard: Urza's Saga, Urza's Legacy, 6th, Urza's Destiny, + // Mercadian Masques, Nemesis, Prophecy. + {"USG", "ULG", "6ED", "UDS", "MMQ", "NMS", "PCY"}, + + // 9th Standard + {"6ED", "MMQ", "NMS", "PCY", "INV", "PLS"}, + + // 10th Standard + {"7ED", "MMQ", "NMS", "PCY", "INV", "PLS", "APC"}, + + // 11th Standard + {"7ED", "INV", "APC", "PLS", "ODY", "TOR", "JUD"}, + + // 12th Standard + {"7ED", "ODY", "TOR", "JUD", "ONS", "LGN", "LGN"}, + + // 13th Standard + {"8ED", "ODY", "TOR", "JUD", "ONS", "LGN", "LGN"}, + + // 14th Standard + {"8ED", "ONS", "LGN", "LGN", "MRD", "DST", "5DN"}, + + // 15th Standard + {"8ED", "MRD", "DST", "5DN", "CHK", "BOK", "SOK"}, + + // 16th Standard + {"9ED", "MRD", "DST", "5DN", "CHK", "BOK", "SOK"}, + + // 17th Standard + {"9ED", "CHK", "BOK", "SOK", "RAV", "GPT", "DIS", "CSP"}, + + // 18th Standard + {"9ED", "RAV", "GPT", "DIS", "CSP", "TSP", "TSB", "PLC", "FUT"}, + + // 19th Standard + {"10E", "RAV", "GPT", "DIS", "CSP", "TSP", "PLC", "FUT"}, + + // 20th Standard + {"10E", "CSP", "TSP", "PLC", "FUT", "LRW", "MOR", "SHM", "EVE"}, + + // 21st Standard + {"10E", "LRW", "MOR", "SHM", "EVE", "ALA", "CON", "ARB"} + }; + + /** + * Constructor. Don't need to mess with any of the sets yet; that will be + * done in the overridden validate function. + */ + public HistoricalStandard() { + super("Constructed - Historical Standard"); + + // banned cards + banned.add("Balance"); + // Not banned in the format, but it is either this or Misty, and most + // people choose Misty. + banned.add("Batterskull"); + banned.add("Memory Jar"); + banned.add("Mind Over Matter"); + banned.add("Mind Twist"); + banned.add("Skullclamp"); + banned.add("Tolarian Academy"); + banned.add("Yawgmoth's Bargain"); + } + + /** + * Overridden validate function. Changes the standard sets, then uses the + * regular validation function to test validity. + * @param deck - the deck to validate. + * @return + */ + @Override + public boolean validate(Deck deck) { + + Map leastInvalid = null; + + boolean valid = false; + + // up to Lorwyn/Alara, standards will have to be hard-coded. + // iterate through the array of standards. + for (String[] sets : standards) { + + // clear the invalid list + invalid.clear(); + + // add the sets to the setCodes. + setCodes = new ArrayList<>(Arrays.asList(sets)); + + // validate it. If it validates, clear the invalid cards and break. + if (super.validate(deck)) { + valid = true; + break; + } + + // if the map holding the invalid cards is empty, set it to a + // copy of the current invalid list. + if (leastInvalid == null) { + leastInvalid = new HashMap<>(this.getInvalid()); + continue; + } + + // see how many invalid cards there are. if there are less invalid + // cards than the stored invalid list, assign the current invalid + // to leastInvalid. + if (leastInvalid.size() > this.getInvalid().size()) { + leastInvalid = new HashMap<>(this.getInvalid()); + } + } + + // After testing the first few standards, do the regular ones. + // set the initial starting and ending date, as well as the current. + GregorianCalendar start = new GregorianCalendar(2006, + Calendar.SEPTEMBER, 1); + GregorianCalendar end = new GregorianCalendar(2008, + Calendar.SEPTEMBER, 1); + GregorianCalendar current = new GregorianCalendar(); + + // use the method for determining regular standard legality, but change + // the date for each standard. + while (end.before(current) && !valid) { + + // clear the invalid list and set codes. + setCodes.clear(); + invalid.clear(); + + // increment the start and end dates. + start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1); + end.set(Calendar.YEAR, start.get(Calendar.YEAR) + 2); + + // Get the sets in that time period. + // (code taken from standard.java) + for (ExpansionSet set : Sets.getInstance().values()) { + if (set.getReleaseDate().after(start.getTime()) + && set.getReleaseDate().before(end.getTime()) + && (set.getSetType() == SetType.CORE || set.getSetType() == SetType.EXPANSION)) { + setCodes.add(set.getCode()); + } + } + + // validate it. If it validates, clear the invalid cards and break. + if (super.validate(deck)) { + invalid.clear(); + valid = true; + break; + } + + // see how many invalid cards there are. if there are less invalid + // cards than the stored invalid list, assign the current invalid + // to leastInvalid. + if (leastInvalid == null){ + leastInvalid = new HashMap<>(this.getInvalid()); + + }else if (leastInvalid.size() > this.getInvalid().size()) { + leastInvalid = new HashMap<>(this.getInvalid()); + } + } + + // if no standard environment is valid, set the invalid to the + // invalid that had the least errors. + if( !valid ){ + this.invalid = new HashMap<>(leastInvalid); + } + + // return the validity. + return valid; + } + +} diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java similarity index 73% rename from Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java rename to Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java index 009c89c6da2..be6caf7dc1a 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AllStandards.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java @@ -15,7 +15,7 @@ import java.util.Map; /** * This class represents a deck conforming to the rules contained in the - * subreddit /r/AllStandards. + subreddit /r/SuperStandard. * * This class was originally made to work with the historical standard ruleset. * Data taken from http://thattournament.website/historic-tournament.php @@ -25,53 +25,15 @@ import java.util.Map; * * @author Marthinwurer (at gmail.com) */ -public class AllStandards extends Constructed { +public class SuperStandard extends Constructed { /* * This array stores the set codes of each standard up to * Kamigawa/Ravnica standard, where rotation stabilized. + * Data taken from http://thattournament.website/historic-tournament.php */ protected static final String[][] standards = { - // the first 10 standards are commented out to adhere to the all - // standards ruleset, instaed of the historical standard ruleset. - -// // 1st standard: The Dark, Fallen Empires, and 4th. -// { "DRK", "FEM", "4ED" }, -// -// // 2nd standard: 4th, Fallen Empires, Ice Age, Chronicles, Homelands, -// // Alliances, and Mirage. -// {"FEM", "4ED", "ICE", "HML", "ALL", "MIR"}, -// -// // 3rd standard: 4th, Chronicles, Alliances, Mirage, Visions. -// {"4ED", "ALL", "MIR", "VIS"}, -// -// // 4th Standard: Ice Age, Homelands, Alliances, Mirage, Visions, 5th, -// // and Weatherlight. -// {"ICE", "HML", "ALL", "MIR", "VIS", "5ED", "WTH"}, -// -// // 5th Standard: Mirage, Visions, 5th, Weatherlight, Tempest, -// // Stronghold, and Exodus. -// {"MIR", "VIS", "5ED", "WTH", "TMP", "STH", "EXO"}, -// -// // 6th Standard: 5th, Tempest, Stronghold, Exodus, Urza's Saga, Urza's -// // Legacy, Urza's Destiny. -// {"5ED", "TMP", "STH", "EXO", "USG", "ULG"}, -// -// // 7th Standard: Tempest, Stronghold, Exodus, Urza's Saga, Urza's -// // Legacy, 6th, Urza's Destiny. -// {"TMP", "STH", "EXO", "USG", "ULG", "6ED", "UDS"}, -// -// // 8th Standard: Urza's Saga, Urza's Legacy, 6th, Urza's Destiny, -// // Mercadian Masques, Nemesis, Prophecy. -// {"USG", "ULG", "6ED", "UDS", "MMQ", "NMS", "PCY"}, -// -// // 9th Standard -// {"6ED", "MMQ", "NMS", "PCY", "INV", "PLS"}, -// -// // 10th Standard -// {"7ED", "MMQ", "NMS", "PCY", "INV", "PLS", "APC"}, - // 11th Standard {"7ED", "INV", "APC", "PLS", "ODY", "TOR", "JUD"}, @@ -110,23 +72,9 @@ public class AllStandards extends Constructed { * Constructor. Don't need to mess with any of the sets yet; that will be * done in the overridden validate function. */ - public AllStandards() { + public SuperStandard() { super("Constructed - All Standards"); - - // banned cards for historical standard ruleset commented out. -// // banned cards -// banned.add("Balance"); -// // Not banned in the format, but it is either this or Misty, and most -// // people choose Misty. -// banned.add("Batterskull"); -// banned.add("Memory Jar"); -// banned.add("Mind Over Matter"); -// banned.add("Mind Twist"); -// banned.add("Skullclamp"); -// banned.add("Tolarian Academy"); -// banned.add("Yawgmoth's Bargain"); - banned.add("Ancient Den"); banned.add("Arcbound Ravager"); banned.add("Disciple of the Vault"); @@ -160,6 +108,16 @@ public class AllStandards extends Constructed { // add the sets to the setCodes. setCodes = new ArrayList<>(Arrays.asList(sets)); + + // if either of the mirrodin blocks are in the time period, ban + // misty and darksteel citadel + if( setCodes.contains("MRD") || setCodes.contains("SOM")){ + banned.add("Darksteel Citadel"); + banned.add("Stoneforge Mystic"); + }else{ + banned.remove("Darksteel Citadel"); + banned.remove("Stoneforge Mystic"); + } // validate it. If it validates, clear the invalid cards and break. if (super.validate(deck)) { From 3e2963d21dafabb0b8588ed0d43e3ed0e2b24f9c Mon Sep 17 00:00:00 2001 From: LoneFox Date: Mon, 27 Jul 2015 07:56:45 +0300 Subject: [PATCH 16/34] 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 17/34] 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 18/34] * 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 19/34] 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 20/34] 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 21/34] 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 22/34] * 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 23/34] * 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; From edd7861fd3473743156c87c8a53c2574a4f99aea Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 21:20:08 +0200 Subject: [PATCH 24/34] * Kalitas, Bloodchief of Ghet - Added missing Legendary supertype. --- .../sets/zendikar/KalitasBloodchiefOfGhet.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/zendikar/KalitasBloodchiefOfGhet.java b/Mage.Sets/src/mage/sets/zendikar/KalitasBloodchiefOfGhet.java index 3e521b30c4d..2e34253f3ee 100644 --- a/Mage.Sets/src/mage/sets/zendikar/KalitasBloodchiefOfGhet.java +++ b/Mage.Sets/src/mage/sets/zendikar/KalitasBloodchiefOfGhet.java @@ -28,10 +28,6 @@ package mage.sets.zendikar; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -40,6 +36,10 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; @@ -53,16 +53,16 @@ public class KalitasBloodchiefOfGhet extends CardImpl { public KalitasBloodchiefOfGhet(UUID ownerId) { super(ownerId, 99, "Kalitas, Bloodchief of Ghet", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); + this.supertype.add("Legendary"); this.expansionSetCode = "ZEN"; this.subtype.add("Vampire"); this.subtype.add("Warrior"); - this.power = new MageInt(5); - this.toughness = new MageInt(5); - // {B}{B}{B}, {T}: Destroy target creature. If that creature dies this way, put a black Vampire creature token onto the battlefield. Its power is equal to that creature's power and its toughness is equal to that creature's toughness. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KalitasDestroyEffect(), new ManaCostsImpl("{B}{B}{B}")); ability.addCost(new TapSourceCost()); + this.power = new MageInt(5); + this.toughness = new MageInt(5); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -114,4 +114,4 @@ class VampireToken extends Token { toughness = new MageInt(tokenToughness); } -} \ No newline at end of file +} From a08e86be97bf89b2f50ab4dfa54afb2f79ebdf2c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 22:11:51 +0200 Subject: [PATCH 25/34] * Recurring Nightmare - Fixed that permanents instead of creatures could be sacrificed to activate the ability. --- Mage.Sets/src/mage/sets/exodus/RecurringNightmare.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/exodus/RecurringNightmare.java b/Mage.Sets/src/mage/sets/exodus/RecurringNightmare.java index 7b1e7534779..200cbf81999 100644 --- a/Mage.Sets/src/mage/sets/exodus/RecurringNightmare.java +++ b/Mage.Sets/src/mage/sets/exodus/RecurringNightmare.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledPermanent; @@ -47,15 +47,14 @@ import mage.target.common.TargetControlledPermanent; * @author jeffwadsworth */ public class RecurringNightmare extends CardImpl { - + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card in your graveyard"); - private static final FilterControlledPermanent filter2 = new FilterControlledPermanent("creature"); + private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("creature"); public RecurringNightmare(UUID ownerId) { super(ownerId, 72, "Recurring Nightmare", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); this.expansionSetCode = "EXO"; - // Sacrifice a creature, Return Recurring Nightmare to its owner's hand: Return target creature card from your graveyard to the battlefield. Activate this ability only any time you could cast a sorcery. Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter2))); ability.addTarget(new TargetCardInYourGraveyard(filter)); From 878909809a5cdf859a3b0a7cadf491e5e45f5ba0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 22:18:43 +0200 Subject: [PATCH 26/34] * Deathforge Shaman - Fixed bug that rollbacks game if multikicker is used. --- Mage.Sets/src/mage/sets/worldwake/DeathforgeShaman.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/DeathforgeShaman.java b/Mage.Sets/src/mage/sets/worldwake/DeathforgeShaman.java index e2fd72a1fac..74cf754a08d 100644 --- a/Mage.Sets/src/mage/sets/worldwake/DeathforgeShaman.java +++ b/Mage.Sets/src/mage/sets/worldwake/DeathforgeShaman.java @@ -27,6 +27,7 @@ */ package mage.sets.worldwake; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -42,8 +43,6 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; -import java.util.UUID; - /** * * @author jeffwadsworth @@ -61,7 +60,7 @@ public class DeathforgeShaman extends CardImpl { // Multikicker {R} this.addAbility(new MultikickerAbility("{R}")); - + // When Deathforge Shaman enters the battlefield, it deals damage to target player equal to twice the number of times it was kicked. Ability ability = new EntersBattlefieldTriggeredAbility(new DeathforgeShamanEffect()); ability.addTarget(new TargetPlayer()); @@ -101,7 +100,7 @@ class DeathforgeShamanEffect extends OneShotEffect { Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { - player.damage(damage, id, game, false, true); + player.damage(damage, source.getSourceId(), game, false, true); return true; } return false; From c49e18049c74119b8809093a63809e837a671da3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 23:13:15 +0200 Subject: [PATCH 27/34] * Orbs of Warding - Fixed possible null pointer exception. --- Mage.Sets/src/mage/sets/magicorigins/OrbsOfWarding.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/magicorigins/OrbsOfWarding.java b/Mage.Sets/src/mage/sets/magicorigins/OrbsOfWarding.java index a244007d356..fc6732b14ce 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/OrbsOfWarding.java +++ b/Mage.Sets/src/mage/sets/magicorigins/OrbsOfWarding.java @@ -89,7 +89,7 @@ class OrbsOfWardingEffect extends PreventionEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getControllerId())) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent.getCardType().contains(CardType.CREATURE)) { + if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { return super.applies(event, source, game); } } From b23d987fe7d1d3ef61dd8d0c2c4d4dec843da7ab Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 27 Jul 2015 23:48:17 +0200 Subject: [PATCH 28/34] * Dead Reckoning - Fixed that source of damage was not set correctly. --- Mage.Sets/src/mage/sets/worldwake/DeadReckoning.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/DeadReckoning.java b/Mage.Sets/src/mage/sets/worldwake/DeadReckoning.java index fcae572f925..9f4235be957 100644 --- a/Mage.Sets/src/mage/sets/worldwake/DeadReckoning.java +++ b/Mage.Sets/src/mage/sets/worldwake/DeadReckoning.java @@ -28,14 +28,13 @@ package mage.sets.worldwake; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; @@ -54,7 +53,6 @@ public class DeadReckoning extends CardImpl { super(ownerId, 56, "Dead Reckoning", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); this.expansionSetCode = "WWK"; - // You may put target creature card from your graveyard on top of your library. If you do, Dead Reckoning deals damage equal to that card's power to target creature. this.getSpellAbility().addEffect(new DeadReckoningEffect()); @@ -103,7 +101,7 @@ class DeadReckoningEffect extends OneShotEffect { int power = creatureInGraveyard.getPower().getValue(); Permanent creature = game.getPermanent(target2.getFirstTarget()); if (creature != null) { - creature.damage(power, id, game, true, true); + creature.damage(power, source.getSourceId(), game, true, true); return true; } } From 1ef74f32938533a38f58c024ce03600e6aa11cc2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 28 Jul 2015 00:04:57 +0200 Subject: [PATCH 29/34] Fixed some possible null pointer exceptions. --- .../src/mage/player/human/HumanPlayer.java | 2 +- .../effects/AuraReplacementEffect.java | 32 ++-- Mage/src/mage/cards/CardImpl.java | 20 ++- Mage/src/mage/game/combat/Combat.java | 31 ++-- Mage/src/mage/game/permanent/Permanent.java | 142 +++++++++++++----- Mage/src/mage/game/permanent/token/Token.java | 16 +- 6 files changed, 164 insertions(+), 79 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 5989d537f46..5997c368ae7 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -251,7 +251,7 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { - if (outcome.equals(Outcome.PutManaInPool)) { + if (Outcome.PutManaInPool.equals(outcome)) { if (currentlyUnpaidMana != null && ManaUtil.tryToAutoSelectAManaColor(choice, currentlyUnpaidMana)) { return true; diff --git a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java index e5cdec0b930..eb62e657c20 100644 --- a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java @@ -51,13 +51,15 @@ import mage.target.common.TargetCardInGraveyard; /** * Cards with the Aura subtype don't change the zone they are in, if there is no * valid target on the battlefield. Also, when entering the battlefield and it - * was not cast (so from Zone != Hand), this effect gets the target to whitch to attach it - * and adds the Aura the the battlefield and attachs it to the target. + * was not cast (so from Zone != Hand), this effect gets the target to whitch to + * attach it and adds the Aura the the battlefield and attachs it to the target. * The "attachTo:" value in game state has to be set therefore. - * - * If no "attachTo:" value is defined, the controlling player has to chose the aura target. * - * This effect is automatically added to ContinuousEffects at the start of a game + * If no "attachTo:" value is defined, the controlling player has to chose the + * aura target. + * + * This effect is automatically added to ContinuousEffects at the start of a + * game * * @author North */ @@ -93,7 +95,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { return false; } // Aura enters the battlefield attached - Object object = game.getState().getValue("attachTo:"+card.getId()); + Object object = game.getState().getValue("attachTo:" + card.getId()); if (object != null && object instanceof PermanentCard) { return false; } @@ -113,7 +115,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { targetId = stackAbility.getEffects().get(0).getTargetPointer().getFirst(game, stackAbility); } } - + if (targetId == null) { Target target = card.getSpellAbility().getTargets().get(0); enchantCardInGraveyard = target instanceof TargetCardInGraveyard; @@ -122,9 +124,10 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } Player player = game.getPlayer(card.getOwnerId()); Outcome auraOutcome = Outcome.BoostCreature; - Ability: for (Ability ability:card.getAbilities()) { + Ability: + for (Ability ability : card.getAbilities()) { if (ability instanceof SpellAbility) { - for (Effect effect: ability.getEffects()) { + for (Effect effect : ability.getEffects()) { if (effect instanceof AttachEffect) { auraOutcome = effect.getOutcome(); break Ability; @@ -167,13 +170,16 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { game.getBattlefield().addPermanent(permanent); card.setZone(Zone.BATTLEFIELD, game); game.applyEffects(); - permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); + boolean entered = permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); game.applyEffects(); + if (!entered) { + return false; + } game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); if (targetCard != null) { permanent.attachTo(targetCard.getId(), game); - } + } if (targetPermanent != null) { targetPermanent.addAttachment(permanent.getId(), game); } @@ -183,7 +189,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } return true; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE; @@ -192,7 +198,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD) - && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND)) ) { + && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND))) { Card card = game.getCard(event.getTargetId()); if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) { return true; diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index db25e8f955b..c353c8e8d31 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -421,13 +421,17 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.setZone(objectId, Zone.BATTLEFIELD); game.setScopeRelevant(true); game.applyEffects(); - permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); + boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); game.setScopeRelevant(false); game.applyEffects(); - if (flag) { - permanent.setTapped(true); + if (entered) { + if (flag) { + permanent.setTapped(true); + } + event.setTarget(permanent); + } else { + return false; } - event.setTarget(permanent); break; default: Card sourceCard = game.getCard(sourceId); @@ -603,11 +607,13 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.setScopeRelevant(true); permanent.setTapped(tapped); permanent.setFaceDown(facedown, game); - permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); + boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); game.setScopeRelevant(false); game.applyEffects(); - game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD)); - return true; + if (entered) { + game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD)); + return true; + } } if (facedown) { this.setFaceDown(false, game); diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 4cc23e31414..1ed22dcf96c 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -810,19 +810,21 @@ public class Combat implements Serializable, Copyable { // check now, if it already blocks a creature that mustBeBlockedByAtLeastOne if (possibleBlocker.getBlocking() > 0) { CombatGroup combatGroupOfPossibleBlocker = findGroupOfBlocker(possibleBlockerId); - for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) { - if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) { - // blocks a creature that has to be blocked by at least one - if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) { - // the creature blocks alone already a creature that has to be blocked by at least one, - // so this is ok - return null; + if (combatGroupOfPossibleBlocker != null) { + for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) { + if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) { + // blocks a creature that has to be blocked by at least one + if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) { + // the creature blocks alone already a creature that has to be blocked by at least one, + // so this is ok + return null; + } + // TODO: Check if the attacker is already blocked by another creature + // and despite there is need that this attacker blocks this attacker also + // I don't know why + Permanent blockedAttacker = game.getPermanent(blockedAttackerId); + return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. "; } - // TODO: Check if the attacker is already blocked by another creature - // and despite there is need that this attacker blocks this attacker also - // I don't know why - Permanent blockedAttacker = game.getPermanent(blockedAttackerId); - return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. "; } } } @@ -947,9 +949,12 @@ public class Combat implements Serializable, Copyable { if (!canDefenderBeAttacked(attackerId, defenderId, game)) { return false; } + Permanent attacker = game.getPermanent(attackerId); + if (attacker == null) { + return false; + } CombatGroup newGroup = new CombatGroup(defenderId, defender != null, defender != null ? defender.getControllerId() : defenderId); newGroup.attackers.add(attackerId); - Permanent attacker = game.getPermanent(attackerId); attacker.setAttacking(true); groups.add(newGroup); return true; diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 33221ea2fb7..9a53acdc2d1 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -1,31 +1,30 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.game.permanent; import java.util.ArrayList; @@ -45,60 +44,89 @@ import mage.game.Game; public interface Permanent extends Card, Controllable { void setControllerId(UUID controllerId); + boolean isTapped(); + boolean untap(Game game); + boolean tap(Game game); + /** * use tap(game) - *

setTapped doesn't trigger TAPPED event and should be used - * only if you want permanent to enter battlefield tapped

+ *

+ * setTapped doesn't trigger TAPPED event and should be used only if you + * want permanent to enter battlefield tapped

* * @param tapped * @deprecated */ @Deprecated void setTapped(boolean tapped); + boolean canTap(); boolean isFlipped(); + boolean unflip(Game game); + boolean flip(Game game); boolean transform(Game game); + boolean isTransformed(); + void setTransformed(boolean value); boolean isPhasedIn(); + boolean phaseIn(Game game); + boolean phaseOut(Game game); boolean isMonstrous(); + void setMonstrous(boolean value); boolean isRenowned(); + void setRenowned(boolean value); void setCardNumber(int cid); + void setExpansionSetCode(String expansionSetCode); + void setRarity(Rarity rarity); + void setFlipCard(boolean flipCard); + void setFlipCardName(String flipCardName); + void setSecondCardFace(Card card); Counters getCounters(); List getAttachments(); + UUID getAttachedTo(); + void attachTo(UUID permanentId, Game game); + boolean addAttachment(UUID permanentId, Game game); + boolean removeAttachment(UUID permanentId, Game game); boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game); + boolean hasProtectionFrom(MageObject source, Game game); + boolean cantBeEnchantedBy(MageObject source, Game game); + boolean wasControlledFromStartOfControllerTurn(); + boolean hasSummoningSickness(); + int getDamage(); + int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable); int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable, ArrayList appliedEffects); @@ -114,58 +142,83 @@ public interface Permanent extends Card, Controllable { * @return */ int markDamage(int damage, UUID sourceId, Game game, boolean preventable, boolean combat); + int applyDamage(Game game); void removeAllDamage(Game game); void reset(Game game); + boolean destroy(UUID sourceId, Game game, boolean noRegen); + boolean sacrifice(UUID sourceId, Game game); + boolean regenerate(UUID sourceId, Game game); + boolean fight(Permanent fightTarget, Ability source, Game game); - void entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent); + boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent); + String getValue(); @Deprecated void addAbility(Ability ability); + @Deprecated void addAbility(Ability ability, Game game); + void addAbility(Ability ability, UUID sourceId, Game game); + void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId); void removeAllAbilities(UUID sourceId, Game game); void addLoyaltyUsed(); + boolean canLoyaltyBeUsed(Game game); - public void resetControl(); + boolean changeControllerId(UUID controllerId, Game game); + boolean checkControlChanged(Game game); void beginningOfTurn(Game game); + void endOfTurn(Game game); + int getTurnsOnBattlefield(); void addPower(int power); + void addToughness(int toughness); boolean isAttacking(); + int getBlocking(); + void setAttacking(boolean attacking); + void setBlocking(int blocking); + int getMaxBlocks(); + void setMaxBlocks(int maxBlocks); + int getMinBlockedBy(); + void setMinBlockedBy(int minBlockedBy); + int getMaxBlockedBy(); + boolean isRemovedFromCombat(); + void setRemovedFromCombat(boolean removedFromCombat); /** * Sets the maximum number of blockers the creature can be blocked by. - * Default = 0 which means there is no restriction in the number of blockers. + * Default = 0 which means there is no restriction in the number of + * blockers. * * @param maxBlockedBy maximum number of blockers */ @@ -180,11 +233,14 @@ public interface Permanent extends Card, Controllable { * @return */ boolean canAttack(UUID defenderId, Game game); + boolean canBlock(UUID attackerId, Game game); + boolean canBlockAny(Game game); /** - * Checks by restriction effects if the permanent can use activated abilities + * Checks by restriction effects if the permanent can use activated + * abilities * * @param game * @return true - permanent can use activated abilities @@ -192,11 +248,15 @@ public interface Permanent extends Card, Controllable { boolean canUseActivatedAbilities(Game game); boolean removeFromCombat(Game game); + boolean removeFromCombat(Game game, boolean withInfo); + boolean isDeathtouched(); /** - * Returns a list of object refrences that dealt damage this turn to this permanent + * Returns a list of object refrences that dealt damage this turn to this + * permanent + * * @return */ HashSet getDealtDamageByThisTurn(); @@ -222,13 +282,14 @@ public interface Permanent extends Card, Controllable { * Get card that was imprinted on this one. * * Can be null if no card was imprinted. + * * @return Imprinted card UUID. */ List getImprinted(); /** - * Allows to connect any card to permanent. - * Very similar to Imprint except that it is for internal use only. + * Allows to connect any card to permanent. Very similar to Imprint except + * that it is for internal use only. * * @param key * @param connectedCard @@ -236,8 +297,9 @@ public interface Permanent extends Card, Controllable { void addConnectedCard(String key, UUID connectedCard); /** - * Returns connected cards. - * Very similar to Imprint except that it is for internal use only. + * Returns connected cards. Very similar to Imprint except that it is for + * internal use only. + * * @param key * @return */ @@ -245,6 +307,7 @@ public interface Permanent extends Card, Controllable { /** * Clear all connected cards. + * * @param key */ void clearConnectedCards(String key); @@ -269,9 +332,11 @@ public interface Permanent extends Card, Controllable { void clearPairedCard(); void setMorphed(boolean value); + boolean isMorphed(); void setManifested(boolean value); + boolean isManifested(); @Override @@ -280,5 +345,6 @@ public interface Permanent extends Card, Controllable { // Simple int counter to set a timewise create order , the lower the number the earlier the object was created // if objects enter the battlefield at the same time they can get (and should) get the same number. int getCreateOrder(); + void setCreateOrder(int createOrder); } diff --git a/Mage/src/mage/game/permanent/token/Token.java b/Mage/src/mage/game/permanent/token/Token.java index def3ce49c65..59d4727dbed 100644 --- a/Mage/src/mage/game/permanent/token/Token.java +++ b/Mage/src/mage/game/permanent/token/Token.java @@ -159,15 +159,17 @@ public class Token extends MageObjectImpl { this.lastAddedTokenId = newToken.getId(); game.setScopeRelevant(true); game.applyEffects(); - newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true); + boolean entered = newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true); game.setScopeRelevant(false); game.applyEffects(); - game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); - if (attacking && game.getCombat() != null) { - game.getCombat().addAttackingCreature(newToken.getId(), game); - } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield"); + if (entered) { + game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); + if (attacking && game.getCombat() != null) { + game.getCombat().addAttackingCreature(newToken.getId(), game); + } + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield"); + } } } From 65ff88d244103d24fd2367d19aaf7ddfedb01ac9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 28 Jul 2015 00:07:51 +0200 Subject: [PATCH 30/34] Fixed some possible null pointer exceptions. --- Mage/src/mage/game/permanent/PermanentImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 1085eeecc8a..93b2f355be7 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -899,10 +899,10 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public void entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) { + public boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) { controlledFromStartOfControllerTurn = false; if (this.isFaceDown(game)) { - // remove some attributes here, bceause first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger + // remove some attributes here, because first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger MorphAbility.setPermanentToFaceDownCreature(this); } EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone); @@ -914,7 +914,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.addSimultaneousEvent(event); } } + return true; } + return false; } @Override From cc9bda92afd5ce077076efc8f7b35fd854a2683c Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Jul 2015 21:40:22 -0400 Subject: [PATCH 31/34] Updated Historical Standard validators Made both actually test for having Stoneforge Mystic XOR Batterskull. Updated Super Standard Banlist. --- .../src/mage/deck/HistoricalStandard.java | 16 ++++++++++++---- .../src/mage/deck/SuperStandard.java | 17 +++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java index 1c8ba55341f..ceb699b1247 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/HistoricalStandard.java @@ -18,7 +18,7 @@ import java.util.Map; * * This class was originally made to work with the historical standard ruleset. * Data taken from http://thattournament.website/historic-tournament.php - * (site changed, originally thtp://mtgt.nfshost.com/historic-tournament.php) + * (site changed, originally http://mtgt.nfshost.com/historic-tournament.php) * * If there are any questions or corrections, feel free to contact me. * @@ -111,9 +111,6 @@ public class HistoricalStandard extends Constructed { // banned cards banned.add("Balance"); - // Not banned in the format, but it is either this or Misty, and most - // people choose Misty. - banned.add("Batterskull"); banned.add("Memory Jar"); banned.add("Mind Over Matter"); banned.add("Mind Twist"); @@ -134,6 +131,17 @@ public class HistoricalStandard extends Constructed { Map leastInvalid = null; boolean valid = false; + + // first, check whether misty and batterskull are in the same deck. + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + if( counts.containsKey("Stoneforge Mystic") + && counts.containsKey("Batterskull")){ + + // if both, then skip all following tests by returning + return false; + } // up to Lorwyn/Alara, standards will have to be hard-coded. // iterate through the array of standards. diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java index be6caf7dc1a..9b6a91dba0f 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/SuperStandard.java @@ -76,10 +76,8 @@ public class SuperStandard extends Constructed { super("Constructed - All Standards"); banned.add("Ancient Den"); - banned.add("Arcbound Ravager"); banned.add("Disciple of the Vault"); banned.add("Great Furnace"); - banned.add("Jace, the Mind Sculptor"); banned.add("Seat of the Synod"); banned.add("Skullclamp"); banned.add("Tree of Tales"); @@ -98,6 +96,17 @@ public class SuperStandard extends Constructed { Map leastInvalid = null; boolean valid = false; + + // first, check whether misty and batterskull are in the same deck. + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + if( counts.containsKey("Stoneforge Mystic") + && counts.containsKey("Batterskull")){ + + // if both, then skip all following tests by returning + return false; + } // up to Lorwyn/Alara, standards will have to be hard-coded. // iterate through the array of standards. @@ -113,10 +122,8 @@ public class SuperStandard extends Constructed { // misty and darksteel citadel if( setCodes.contains("MRD") || setCodes.contains("SOM")){ banned.add("Darksteel Citadel"); - banned.add("Stoneforge Mystic"); }else{ banned.remove("Darksteel Citadel"); - banned.remove("Stoneforge Mystic"); } // validate it. If it validates, clear the invalid cards and break. @@ -174,10 +181,8 @@ public class SuperStandard extends Constructed { // misty and darksteel citadel if( setCodes.contains("MRD") || setCodes.contains("SOM")){ banned.add("Darksteel Citadel"); - banned.add("Stoneforge Mystic"); }else{ banned.remove("Darksteel Citadel"); - banned.remove("Stoneforge Mystic"); } // validate it. If it validates, clear the invalid cards and break. From 409edab4eca424f2034fa504b058d2115d657155 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Jul 2015 21:41:07 -0400 Subject: [PATCH 32/34] Added Historical and Super Standard Validators to default configs --- Mage.Server/config/config.xml | 2 ++ Mage.Server/release/config/config.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 453ddaffeb0..e28563936ef 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -87,7 +87,9 @@ + + diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index c18cbdeb3af..93ccc1a86a3 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -66,7 +66,9 @@ + + From 1f24b569c73758ff18e997fc2b676fc1e6a328d9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 28 Jul 2015 15:04:37 +0200 Subject: [PATCH 33/34] * Fixed a bug of adding abilities to non permanentes if the an effect added an ability to multiple objects. --- Mage/src/mage/game/GameState.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index b7b9c1850b7..21824f7a41c 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -41,6 +41,7 @@ import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbilities; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.MageSingleton; import mage.abilities.Mode; import mage.abilities.SpecialActions; import mage.abilities.StaticAbility; @@ -856,13 +857,19 @@ public class GameState implements Serializable, Copyable { * @param ability */ public void addOtherAbility(Card attachedTo, Ability ability) { - ability.setSourceId(attachedTo.getId()); - ability.setControllerId(attachedTo.getOwnerId()); + Ability newAbility; + if (ability instanceof MageSingleton) { + newAbility = ability; + } else { + newAbility = ability.copy(); + } + newAbility.setSourceId(attachedTo.getId()); + newAbility.setControllerId(attachedTo.getOwnerId()); if (!cardState.containsKey(attachedTo.getId())) { cardState.put(attachedTo.getId(), new CardState()); } - cardState.get(attachedTo.getId()).addAbility(ability); - addAbility(ability, attachedTo.getId(), attachedTo); + cardState.get(attachedTo.getId()).addAbility(newAbility); + addAbility(newAbility, attachedTo.getId(), attachedTo); } /** From e5d29907ed432f05b3bc39e2fff8a16923cc454f Mon Sep 17 00:00:00 2001 From: fireshoes Date: Tue, 28 Jul 2015 12:40:34 -0500 Subject: [PATCH 34/34] Implemented Righteous Aura, Haazda Shield Mate, Invulnerability, and Cho-Arrim Alchemist --- .../sets/dissension/HaazdaShieldMate.java | 73 ++++++++++ .../mercadianmasques/ChoArrimAlchemist.java | 134 ++++++++++++++++++ .../sets/mercadianmasques/RighteousAura.java | 54 +++++++ .../mage/sets/tempest/Invulnerability.java | 64 +++++++++ .../src/mage/sets/visions/RighteousAura.java | 66 +++++++++ 5 files changed, 391 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/dissension/HaazdaShieldMate.java create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/ChoArrimAlchemist.java create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/RighteousAura.java create mode 100644 Mage.Sets/src/mage/sets/tempest/Invulnerability.java create mode 100644 Mage.Sets/src/mage/sets/visions/RighteousAura.java diff --git a/Mage.Sets/src/mage/sets/dissension/HaazdaShieldMate.java b/Mage.Sets/src/mage/sets/dissension/HaazdaShieldMate.java new file mode 100644 index 00000000000..54c0f540246 --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/HaazdaShieldMate.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.dissension; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; + +/** + * + * @author anonymous + */ +public class HaazdaShieldMate extends CardImpl { + + public HaazdaShieldMate(UUID ownerId) { + super(ownerId, 12, "Haazda Shield Mate", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "DIS"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // At the beginning of your upkeep, sacrifice Haazda Shield Mate unless you pay {W}{W}. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{W}{W}")), TargetController.YOU, false)); + + // {W}: The next time a source of your choice would deal damage to you this turn, prevent that damage. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new ManaCostsImpl("{W}"))); + } + + public HaazdaShieldMate(final HaazdaShieldMate card) { + super(card); + } + + @Override + public HaazdaShieldMate copy() { + return new HaazdaShieldMate(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/ChoArrimAlchemist.java b/Mage.Sets/src/mage/sets/mercadianmasques/ChoArrimAlchemist.java new file mode 100644 index 00000000000..27d31e4d615 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/ChoArrimAlchemist.java @@ -0,0 +1,134 @@ +/* + * 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.mercadianmasques; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.PreventionEffectImpl; +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.players.Player; +import mage.target.TargetSource; + +/** + * + * @author anonymous + */ +public class ChoArrimAlchemist extends CardImpl { + + public ChoArrimAlchemist(UUID ownerId) { + super(ownerId, 8, "Cho-Arrim Alchemist", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{W}"); + this.expansionSetCode = "MMQ"; + this.subtype.add("Human"); + this.subtype.add("Spellshaper"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{W}{W}, {tap}, Discard a card: The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChoArrimAlchemistEffect(), new ManaCostsImpl("{1}{W}{W}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + this.addAbility(ability); + } + + public ChoArrimAlchemist(final ChoArrimAlchemist card) { + super(card); + } + + @Override + public ChoArrimAlchemist copy() { + return new ChoArrimAlchemist(this); + } +} + +class ChoArrimAlchemistEffect extends PreventionEffectImpl { + + private final TargetSource target; + + public ChoArrimAlchemistEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way."; + this.target = new TargetSource(); + } + + public ChoArrimAlchemistEffect(final ChoArrimAlchemistEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + @Override + public ChoArrimAlchemistEffect copy() { + return new ChoArrimAlchemistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public void init(Ability source, Game game) { + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + PreventionEffectData preventionData = preventDamageAction(event, source, game); + this.used = true; + this.discard(); // only one use + if (preventionData.getPreventedDamage() > 0) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.gainLife(preventionData.getPreventedDamage(), game); + } + } + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!this.used && super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(target.getFirstTarget())) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/RighteousAura.java b/Mage.Sets/src/mage/sets/mercadianmasques/RighteousAura.java new file mode 100644 index 00000000000..dafd9416aa8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/RighteousAura.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.mercadianmasques; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author anonymous + */ +public class RighteousAura extends mage.sets.visions.RighteousAura { + + public RighteousAura(UUID ownerId) { + super(ownerId); + this.cardNumber = 45; + this.expansionSetCode = "MMQ"; + this.rarity = Rarity.UNCOMMON; + } + + public RighteousAura(final RighteousAura card) { + super(card); + } + + @Override + public RighteousAura copy() { + return new RighteousAura(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/Invulnerability.java b/Mage.Sets/src/mage/sets/tempest/Invulnerability.java new file mode 100644 index 00000000000..7c278d62dea --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/Invulnerability.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.tempest; + +import java.util.UUID; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.keyword.BuybackAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; + +/** + * + * @author anonymous + */ +public class Invulnerability extends CardImpl { + + public Invulnerability(UUID ownerId) { + super(ownerId, 237, "Invulnerability", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); + this.expansionSetCode = "TMP"; + + // Buyback {3} + this.addAbility(new BuybackAbility("{3}")); + + // The next time a source of your choice would deal damage to you this turn, prevent that damage. + this.getSpellAbility().addEffect(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn)); + } + + public Invulnerability(final Invulnerability card) { + super(card); + } + + @Override + public Invulnerability copy() { + return new Invulnerability(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/visions/RighteousAura.java b/Mage.Sets/src/mage/sets/visions/RighteousAura.java new file mode 100644 index 00000000000..fd13176fe8b --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/RighteousAura.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.visions; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author anonymous + */ +public class RighteousAura extends CardImpl { + + public RighteousAura(UUID ownerId) { + super(ownerId, 120, "Righteous Aura", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "VIS"; + + // {W}, Pay 2 life: The next time a source of your choice would deal damage to you this turn, prevent that damage. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new ManaCostsImpl("{W}")); + ability.addCost(new PayLifeCost(2)); + this.addAbility(ability); + } + + public RighteousAura(final RighteousAura card) { + super(card); + } + + @Override + public RighteousAura copy() { + return new RighteousAura(this); + } +}