From 58571c6d1e8d10c18105c05ab98d620726731fd4 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Tue, 21 Jul 2015 19:35:34 +0300 Subject: [PATCH 01/15] Implement cards: Horned Cheetah, Llanowar Vanguard, Rampant Elephant, and Restrain --- .../src/mage/sets/invasion/HornedCheetah.java | 62 +++++++++++++++++ .../mage/sets/invasion/LlanowarVanguard.java | 67 ++++++++++++++++++ .../mage/sets/invasion/RampantElephant.java | 69 +++++++++++++++++++ .../src/mage/sets/invasion/Restrain.java | 67 ++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/invasion/HornedCheetah.java create mode 100644 Mage.Sets/src/mage/sets/invasion/LlanowarVanguard.java create mode 100644 Mage.Sets/src/mage/sets/invasion/RampantElephant.java create mode 100644 Mage.Sets/src/mage/sets/invasion/Restrain.java diff --git a/Mage.Sets/src/mage/sets/invasion/HornedCheetah.java b/Mage.Sets/src/mage/sets/invasion/HornedCheetah.java new file mode 100644 index 0000000000..ce0fe58daa --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/HornedCheetah.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.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.DealsDamageGainLifeSourceTriggeredAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class HornedCheetah extends CardImpl { + + public HornedCheetah(UUID ownerId) { + super(ownerId, 251, "Horned Cheetah", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + this.expansionSetCode = "INV"; + this.subtype.add("Cat"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Horned Cheetah deals damage, you gain that much life. + this.addAbility(new DealsDamageGainLifeSourceTriggeredAbility()); + } + + public HornedCheetah(final HornedCheetah card) { + super(card); + } + + @Override + public HornedCheetah copy() { + return new HornedCheetah(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/LlanowarVanguard.java b/Mage.Sets/src/mage/sets/invasion/LlanowarVanguard.java new file mode 100644 index 0000000000..2a64d4a36b --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/LlanowarVanguard.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author LoneFox + */ +public class LlanowarVanguard extends CardImpl { + + public LlanowarVanguard(UUID ownerId) { + super(ownerId, 197, "Llanowar Vanguard", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "INV"; + this.subtype.add("Dryad"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Llanowar Vanguard gets +0/+4 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, + new BoostSourceEffect(0, 4, Duration.EndOfTurn), new TapSourceCost())); + } + + public LlanowarVanguard(final LlanowarVanguard card) { + super(card); + } + + @Override + public LlanowarVanguard copy() { + return new LlanowarVanguard(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/RampantElephant.java b/Mage.Sets/src/mage/sets/invasion/RampantElephant.java new file mode 100644 index 0000000000..2d65858275 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/RampantElephant.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.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class RampantElephant extends CardImpl { + + public RampantElephant(UUID ownerId) { + super(ownerId, 28, "Rampant Elephant", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.expansionSetCode = "INV"; + this.subtype.add("Elephant"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {G}: Target creature blocks Rampant Elephant this turn if able. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(), new ManaCostsImpl("{G}")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public RampantElephant(final RampantElephant card) { + super(card); + } + + @Override + public RampantElephant copy() { + return new RampantElephant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/Restrain.java b/Mage.Sets/src/mage/sets/invasion/Restrain.java new file mode 100644 index 0000000000..02883862e1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/Restrain.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.invasion; + +import java.util.UUID; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetAttackingCreature; + +/** + * + * @author LoneFox + */ +public class Restrain extends CardImpl { + + public Restrain(UUID ownerId) { + super(ownerId, 30, "Restrain", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); + this.expansionSetCode = "INV"; + + // Prevent all combat damage that would be dealt by target attacking creature this turn. + Effect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, true); + effect.setText("Prevent all combat damage that would be dealt by target attacking creature this turn"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } + + public Restrain(final Restrain card) { + super(card); + } + + @Override + public Restrain copy() { + return new Restrain(this); + } +} From 9acff5aa9cfad37110a3405f98b895b5eec06a70 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Tue, 21 Jul 2015 21:51:27 +0300 Subject: [PATCH 02/15] Add DamageDealtToAttachedTriggeredAbility an use it for existing cards. Implement cards: Binding Agony and Soul Link --- .../src/mage/sets/apocalypse/SoulLink.java | 78 +++++++++++++++ .../sets/championsofkamigawa/RaggedVeins.java | 94 ++---------------- .../sets/darkascension/SpitefulShadows.java | 48 +-------- .../src/mage/sets/mirage/BindingAgony.java | 78 +++++++++++++++ .../src/mage/sets/odyssey/DruidsCall.java | 93 ++---------------- ...DamageDealtToAttachedTriggeredAbility.java | 98 +++++++++++++++++++ 6 files changed, 276 insertions(+), 213 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/SoulLink.java create mode 100644 Mage.Sets/src/mage/sets/mirage/BindingAgony.java create mode 100644 Mage/src/mage/abilities/common/DamageDealtToAttachedTriggeredAbility.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/SoulLink.java b/Mage.Sets/src/mage/sets/apocalypse/SoulLink.java new file mode 100644 index 0000000000..dbfb8ef751 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/SoulLink.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.apocalypse; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageAttachedTriggeredAbility; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class SoulLink extends CardImpl { + + public SoulLink(UUID ownerId) { + super(ownerId, 120, "Soul Link", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{B}"); + this.expansionSetCode = "APC"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Whenever enchanted creature deals damage, you gain that much life. + this.addAbility(new DealsDamageAttachedTriggeredAbility(Zone.BATTLEFIELD, + new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); + // Whenever enchanted creature is dealt damage, you gain that much life. + this.addAbility(new DamageDealtToAttachedTriggeredAbility(new GainLifeEffect(new NumericSetToEffectValues("that much", "damage")), false)); + } + + public SoulLink(final SoulLink card) { + super(card); + } + + @Override + public SoulLink copy() { + return new SoulLink(this); + } +} diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/RaggedVeins.java b/Mage.Sets/src/mage/sets/championsofkamigawa/RaggedVeins.java index 551f3c750d..2f75078d46 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/RaggedVeins.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/RaggedVeins.java @@ -30,24 +30,21 @@ package mage.sets.championsofkamigawa; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -67,11 +64,13 @@ public class RaggedVeins extends CardImpl { // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Whenever enchanted creature is dealt damage, its controller loses that much life. - this.addAbility(new RaggedVeinsTriggeredAbility()); + Effect effect = new LoseLifeTargetEffect(new NumericSetToEffectValues("that much", "damage")); + effect.setText("its controller loses that much life"); + this.addAbility(new DamageDealtToAttachedTriggeredAbility(Zone.BATTLEFIELD, effect, false, SetTargetPointer.PLAYER)); } public RaggedVeins(final RaggedVeins card) { @@ -83,78 +82,3 @@ public class RaggedVeins extends CardImpl { return new RaggedVeins(this); } } - -class RaggedVeinsTriggeredAbility extends TriggeredAbilityImpl { - - public RaggedVeinsTriggeredAbility() { - super(Zone.BATTLEFIELD, new RaggedVeinsEffect()); - } - - public RaggedVeinsTriggeredAbility(final RaggedVeinsTriggeredAbility ability) { - super(ability); - } - - @Override - public RaggedVeinsTriggeredAbility copy() { - return new RaggedVeinsTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - UUID targetId = event.getTargetId(); - if (enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(targetId)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature is dealt damage, its controller loses that much life."; - } -} - -class RaggedVeinsEffect extends OneShotEffect { - - public RaggedVeinsEffect() { - super(Outcome.Damage); - this.staticText = "its controller loses that much life"; - } - - public RaggedVeinsEffect(final RaggedVeinsEffect effect) { - super(effect); - } - - @Override - public RaggedVeinsEffect copy() { - return new RaggedVeinsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer damageAmount = (Integer) this.getValue("damageAmount"); - UUID targetId = this.targetPointer.getFirst(game, source); - if (damageAmount != null && targetId != null) { - Permanent permanent = game.getPermanent(targetId); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.loseLife(damageAmount, game); - return true; - } - } - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/darkascension/SpitefulShadows.java b/Mage.Sets/src/mage/sets/darkascension/SpitefulShadows.java index a874f84736..942dc372f5 100644 --- a/Mage.Sets/src/mage/sets/darkascension/SpitefulShadows.java +++ b/Mage.Sets/src/mage/sets/darkascension/SpitefulShadows.java @@ -29,7 +29,7 @@ package mage.sets.darkascension; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; @@ -37,10 +37,9 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -65,7 +64,8 @@ public class SpitefulShadows extends CardImpl { this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Whenever enchanted creature is dealt damage, it deals that much damage to its controller. - this.addAbility(new SpitefulShadowsTriggeredAbility()); + this.addAbility(new DamageDealtToAttachedTriggeredAbility(Zone.BATTLEFIELD, new SpitefulShadowsEffect(), + false, SetTargetPointer.PERMANENT)); } public SpitefulShadows(final SpitefulShadows card) { @@ -78,44 +78,6 @@ public class SpitefulShadows extends CardImpl { } } -class SpitefulShadowsTriggeredAbility extends TriggeredAbilityImpl { - - public SpitefulShadowsTriggeredAbility() { - super(Zone.BATTLEFIELD, new SpitefulShadowsEffect()); - } - - public SpitefulShadowsTriggeredAbility(final SpitefulShadowsTriggeredAbility ability) { - super(ability); - } - - @Override - public SpitefulShadowsTriggeredAbility copy() { - return new SpitefulShadowsTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - UUID targetId = event.getTargetId(); - if (enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(targetId)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature is dealt damage, it deals that much damage to its controller."; - } -} - class SpitefulShadowsEffect extends OneShotEffect { public SpitefulShadowsEffect() { @@ -134,7 +96,7 @@ class SpitefulShadowsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Integer damageAmount = (Integer) this.getValue("damageAmount"); + Integer damageAmount = (Integer) this.getValue("damage"); if (damageAmount != null) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent == null) { diff --git a/Mage.Sets/src/mage/sets/mirage/BindingAgony.java b/Mage.Sets/src/mage/sets/mirage/BindingAgony.java new file mode 100644 index 0000000000..5be6d6400a --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/BindingAgony.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.mirage; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class BindingAgony extends CardImpl { + + public BindingAgony(UUID ownerId) { + super(ownerId, 4, "Binding Agony", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.expansionSetCode = "MIR"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Whenever enchanted creature is dealt damage, Binding Agony deals that much damage to that creature's controller. + Effect effect = new DamageTargetEffect(new NumericSetToEffectValues("that much", "damage")); + effect.setText("{this} deals that much damage to that creature's controller"); + this.addAbility(new DamageDealtToAttachedTriggeredAbility(Zone.BATTLEFIELD, effect, false, SetTargetPointer.PLAYER)); + } + + public BindingAgony(final BindingAgony card) { + super(card); + } + + @Override + public BindingAgony copy() { + return new BindingAgony(this); + } +} diff --git a/Mage.Sets/src/mage/sets/odyssey/DruidsCall.java b/Mage.Sets/src/mage/sets/odyssey/DruidsCall.java index 7746b54f02..3f7f6c922d 100644 --- a/Mage.Sets/src/mage/sets/odyssey/DruidsCall.java +++ b/Mage.Sets/src/mage/sets/odyssey/DruidsCall.java @@ -29,25 +29,22 @@ package mage.sets.odyssey; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DamageDealtToAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.NumericSetToEffectValues; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.game.permanent.token.SquirrelToken; -import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -69,7 +66,9 @@ public class DruidsCall extends CardImpl { this.addAbility(ability); // Whenever enchanted creature is dealt damage, its controller puts that many 1/1 green Squirrel creature tokens onto the battlefield. - this.addAbility(new DruidsCallTriggeredAbility()); + Effect effect = new CreateTokenTargetEffect(new SquirrelToken(), new NumericSetToEffectValues("that much", "damage")); + effect.setText("its controller puts that many 1/1 green Squirrel creature tokens onto the battlefield"); + this.addAbility(new DamageDealtToAttachedTriggeredAbility(Zone.BATTLEFIELD, effect, false, SetTargetPointer.PLAYER)); } public DruidsCall(final DruidsCall card) { @@ -81,79 +80,3 @@ public class DruidsCall extends CardImpl { return new DruidsCall(this); } } - -class DruidsCallTriggeredAbility extends TriggeredAbilityImpl { - - public DruidsCallTriggeredAbility() { - super(Zone.BATTLEFIELD, new DruidsCallEffect()); - } - - public DruidsCallTriggeredAbility(final DruidsCallTriggeredAbility ability) { - super(ability); - } - - @Override - public DruidsCallTriggeredAbility copy() { - return new DruidsCallTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DAMAGED_CREATURE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - UUID targetId = event.getTargetId(); - if (enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(targetId)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature is dealt damage, its controller puts that many 1/1 green Squirrel creature tokens onto the battlefield."; - } -} - -class DruidsCallEffect extends OneShotEffect { - - public DruidsCallEffect() { - super(Outcome.Damage); - this.staticText = "its controller puts that many 1/1 green Squirrel creature tokens onto the battlefield"; - } - - public DruidsCallEffect(final DruidsCallEffect effect) { - super(effect); - } - - @Override - public DruidsCallEffect copy() { - return new DruidsCallEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer damageAmount = (Integer) this.getValue("damageAmount"); - UUID targetId = this.targetPointer.getFirst(game, source); - if (damageAmount != null && targetId != null) { - Permanent permanent = game.getPermanent(targetId); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(targetId, Zone.BATTLEFIELD); - } - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - Token token = new SquirrelToken(); - token.putOntoBattlefield(damageAmount, game, source.getSourceId(), player.getId()); - return true; - } - } - } - return false; - } -} diff --git a/Mage/src/mage/abilities/common/DamageDealtToAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/DamageDealtToAttachedTriggeredAbility.java new file mode 100644 index 0000000000..90e4b5fb26 --- /dev/null +++ b/Mage/src/mage/abilities/common/DamageDealtToAttachedTriggeredAbility.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.common; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent.EventType; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LoneFox + */ +public class DamageDealtToAttachedTriggeredAbility extends TriggeredAbilityImpl { + + protected SetTargetPointer setTargetPointer; + + public DamageDealtToAttachedTriggeredAbility(Effect effect, boolean optional) { + this(Zone.BATTLEFIELD, effect, optional, SetTargetPointer.NONE); + } + + public DamageDealtToAttachedTriggeredAbility(Zone zone, Effect effect, boolean optional, SetTargetPointer setTargetPointer) { + super(zone, effect, optional); + this.setTargetPointer = setTargetPointer; + } + + public DamageDealtToAttachedTriggeredAbility(final DamageDealtToAttachedTriggeredAbility ability) { + super(ability); + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DamageDealtToAttachedTriggeredAbility copy() { + return new DamageDealtToAttachedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_CREATURE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent enchantment = game.getPermanent(sourceId); + UUID targetId = event.getTargetId(); + if(enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) { + for(Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + switch(setTargetPointer) { + case PERMANENT: + effect.setTargetPointer(new FixedTarget(targetId)); + break; + case PLAYER: + effect.setTargetPointer(new FixedTarget(game.getPermanentOrLKIBattlefield(targetId).getControllerId())); + break; + } + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever enchanted creature is dealt damage, " + super.getRule(); + } +} From 5da1efa877a03568287f7457f05f1047ad2af623 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Wed, 22 Jul 2015 12:22:47 +0300 Subject: [PATCH 03/15] Implement cards: Foul Presence, Fungal Shambler, Shimmering Mirage, and Suffocating Blast --- .../mage/sets/apocalypse/FoulPresence.java | 87 +++++++++++++++++++ .../mage/sets/apocalypse/FungalShambler.java | 76 ++++++++++++++++ .../sets/apocalypse/ShimmeringMirage.java | 64 ++++++++++++++ .../sets/apocalypse/SuffocatingBlast.java | 67 ++++++++++++++ .../sets/prereleaseevents/FungalShambler.java | 54 ++++++++++++ 5 files changed, 348 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/FoulPresence.java create mode 100644 Mage.Sets/src/mage/sets/apocalypse/FungalShambler.java create mode 100644 Mage.Sets/src/mage/sets/apocalypse/ShimmeringMirage.java create mode 100644 Mage.Sets/src/mage/sets/apocalypse/SuffocatingBlast.java create mode 100644 Mage.Sets/src/mage/sets/prereleaseevents/FungalShambler.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/FoulPresence.java b/Mage.Sets/src/mage/sets/apocalypse/FoulPresence.java new file mode 100644 index 0000000000..67ad72ed4e --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/FoulPresence.java @@ -0,0 +1,87 @@ +/* + * 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.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class FoulPresence extends CardImpl { + + public FoulPresence(UUID ownerId) { + super(ownerId, 39, "Foul Presence", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + this.expansionSetCode = "APC"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature gets -1/-1 and has "{T}: Target creature gets -1/-1 until end of turn." + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(-1, -1, Duration.WhileOnBattlefield)); + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new BoostTargetEffect(-1, -1, Duration.EndOfTurn), new TapSourceCost()); + gainedAbility.addTarget(new TargetCreaturePermanent()); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA, Duration.WhileOnBattlefield); + effect.setText("and has \"{T}: Target creature gets -1/-1 until end of turn.\""); + ability.addEffect(effect); + this.addAbility(ability); + } + + public FoulPresence(final FoulPresence card) { + super(card); + } + + @Override + public FoulPresence copy() { + return new FoulPresence(this); + } +} diff --git a/Mage.Sets/src/mage/sets/apocalypse/FungalShambler.java b/Mage.Sets/src/mage/sets/apocalypse/FungalShambler.java new file mode 100644 index 0000000000..e8f9d48bd5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/FungalShambler.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.apocalypse; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DealsDamageToOpponentTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class FungalShambler extends CardImpl { + + public FungalShambler(UUID ownerId) { + super(ownerId, 100, "Fungal Shambler", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{G}{U}{B}"); + this.expansionSetCode = "APC"; + this.subtype.add("Fungus"); + this.subtype.add("Beast"); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + // Whenever Fungal Shambler deals damage to an opponent, you draw a card and that opponent discards a card. + Effect effect = new DrawCardSourceControllerEffect(1); + effect.setText("you draw a card"); + Ability ability = new DealsDamageToOpponentTriggeredAbility(effect, false); + effect = new DiscardTargetEffect(1); + effect.setText("and that opponent discards a card"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public FungalShambler(final FungalShambler card) { + super(card); + } + + @Override + public FungalShambler copy() { + return new FungalShambler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/apocalypse/ShimmeringMirage.java b/Mage.Sets/src/mage/sets/apocalypse/ShimmeringMirage.java new file mode 100644 index 0000000000..34e9fd5680 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/ShimmeringMirage.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.apocalypse; + +import java.util.UUID; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author LoneFox + */ +public class ShimmeringMirage extends CardImpl { + + public ShimmeringMirage(UUID ownerId) { + super(ownerId, 30, "Shimmering Mirage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "APC"; + + // Target land becomes the basic land type of your choice until end of turn. + this.getSpellAbility().addEffect(new BecomesBasicLandTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetLandPermanent()); + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + } + + public ShimmeringMirage(final ShimmeringMirage card) { + super(card); + } + + @Override + public ShimmeringMirage copy() { + return new ShimmeringMirage(this); + } +} diff --git a/Mage.Sets/src/mage/sets/apocalypse/SuffocatingBlast.java b/Mage.Sets/src/mage/sets/apocalypse/SuffocatingBlast.java new file mode 100644 index 0000000000..b0e3f6ee67 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/SuffocatingBlast.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.apocalypse; + +import java.util.UUID; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.TargetSpell; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class SuffocatingBlast extends CardImpl { + + public SuffocatingBlast(UUID ownerId) { + super(ownerId, 124, "Suffocating Blast", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{U}{U}{R}"); + this.expansionSetCode = "APC"; + + // Counter target spell and Suffocating Blast deals 3 damage to target creature. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + Effect effect = new DamageTargetEffect(3); + effect.setText("and {this} deals 3 damage to target creature"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public SuffocatingBlast(final SuffocatingBlast card) { + super(card); + } + + @Override + public SuffocatingBlast copy() { + return new SuffocatingBlast(this); + } +} diff --git a/Mage.Sets/src/mage/sets/prereleaseevents/FungalShambler.java b/Mage.Sets/src/mage/sets/prereleaseevents/FungalShambler.java new file mode 100644 index 0000000000..5bd1f2ef1b --- /dev/null +++ b/Mage.Sets/src/mage/sets/prereleaseevents/FungalShambler.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.prereleaseevents; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LoneFox + */ +public class FungalShambler extends mage.sets.apocalypse.FungalShambler { + + public FungalShambler(UUID ownerId) { + super(ownerId); + this.cardNumber = 14; + this.expansionSetCode = "PTC"; + this.rarity = Rarity.SPECIAL; + } + + public FungalShambler(final FungalShambler card) { + super(card); + } + + @Override + public FungalShambler copy() { + return new FungalShambler(this); + } +} From ae675942e2b2018cf714b60d513f45c03f81a97d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 22 Jul 2015 17:23:07 +0200 Subject: [PATCH 04/15] Added Dwarven Landslide. --- .../sets/apocalypse/DwarvenLandslide.java | 80 +++++++++++++++++++ .../costs/OptionalAdditionalCost.java | 14 ++-- .../mage/abilities/keyword/KickerAbility.java | 43 ++++++---- 3 files changed, 114 insertions(+), 23 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/apocalypse/DwarvenLandslide.java diff --git a/Mage.Sets/src/mage/sets/apocalypse/DwarvenLandslide.java b/Mage.Sets/src/mage/sets/apocalypse/DwarvenLandslide.java new file mode 100644 index 0000000000..4edae4f564 --- /dev/null +++ b/Mage.Sets/src/mage/sets/apocalypse/DwarvenLandslide.java @@ -0,0 +1,80 @@ +/* + * 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.Ability; +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledLandPermanent; +import mage.game.Game; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author LevelX2 + */ +public class DwarvenLandslide extends CardImpl { + + public DwarvenLandslide(UUID ownerId) { + super(ownerId, 60, "Dwarven Landslide", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{3}{R}"); + this.expansionSetCode = "APC"; + + // Kicker-{2}{R}, Sacrifice a land. + Costs kickerCosts = new CostsImpl<>(); + kickerCosts.add(new ManaCostsImpl<>("{2}{R}")); + kickerCosts.add(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land")))); + this.addAbility(new KickerAbility(kickerCosts)); + // Destroy target land. If Dwarven Landslide was kicked, destroy another target land. + getSpellAbility().addEffect(new DestroyTargetEffect("Destroy target land. If {this} was kicked, destroy another target land")); + } + + public DwarvenLandslide(final DwarvenLandslide card) { + super(card); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.addTarget(new TargetLandPermanent(KickedCondition.getInstance().apply(game, ability) ? 2 : 1)); + } + + @Override + public DwarvenLandslide copy() { + return new DwarvenLandslide(this); + } +} diff --git a/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java b/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java index 75f75c5a41..949bbdfe45 100644 --- a/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java +++ b/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java @@ -25,13 +25,12 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.costs; /** * @author LevelX2 */ -public interface OptionalAdditionalCost extends Cost { +public interface OptionalAdditionalCost extends Costs { String getName(); @@ -52,15 +51,15 @@ public interface OptionalAdditionalCost extends Cost { String getReminderText(); /** - * Returns a text suffix for the game log, that can be added to - * the cast message. + * Returns a text suffix for the game log, that can be added to the cast + * message. * - * @param position - if there are multiple costs, it's the postion the cost is set (starting with 0) + * @param position - if there are multiple costs, it's the postion the cost + * is set (starting with 0) * @return */ String getCastSuffixMessage(int position); - /** * If the player intends to pay the cost, the cost will be activated * @@ -96,8 +95,9 @@ public interface OptionalAdditionalCost extends Cost { /** * Returns the number of times the cost was activated + * * @return */ int getActivateCount(); - + } diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index bab4178281..b262a3cc29 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -37,6 +37,7 @@ import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; import mage.abilities.costs.OptionalAdditionalCost; import mage.abilities.costs.OptionalAdditionalCostImpl; import mage.abilities.costs.OptionalAdditionalSourceCosts; @@ -217,26 +218,16 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo if (kickerCost.canPay(ability, sourceId, controllerId, game) && player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) { this.activateKicker(kickerCost, ability, game); - for (Iterator it = ((Costs) kickerCost).iterator(); it.hasNext();) { - Cost cost = (Cost) it.next(); - if (cost instanceof ManaCostsImpl) { - List varCosts = ((ManaCostsImpl) cost).getVariableCosts(); - if (!varCosts.isEmpty()) { - // use only first variable cost - xManaValue = game.getPlayer(this.controllerId).announceXMana(varCosts.get(0).getMinX(), Integer.MAX_VALUE, "Announce kicker value for " + varCosts.get(0).getText(), game, this); - // kicker variable X costs handled internally as multikicker with {1} cost (no multikicker on card) - if (!game.isSimulation()) { - game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue + " for " + " kicker X "); - } - ability.getManaCostsToPay().add(new GenericManaCost(xManaValue)); - } else { - ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + for (Iterator itKickerCost = kickerCost.iterator(); itKickerCost.hasNext();) { + Object kickerCostObject = itKickerCost.next(); + if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) { + for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) { + addKickerCostsToAbility(itDetails.next(), ability, game); } } else { - ability.getCosts().add(cost.copy()); + addKickerCostsToAbility((Cost) kickerCostObject, ability, game); } } - again = kickerCost.isRepeatable(); } else { again = false; @@ -247,6 +238,26 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } } + private void addKickerCostsToAbility(Cost cost, Ability ability, Game game) { + if (cost instanceof ManaCostsImpl) { + @SuppressWarnings("unchecked") + List varCosts = ((ManaCostsImpl) cost).getVariableCosts(); + if (!varCosts.isEmpty()) { + // use only first variable cost + xManaValue = game.getPlayer(this.controllerId).announceXMana(varCosts.get(0).getMinX(), Integer.MAX_VALUE, "Announce kicker value for " + varCosts.get(0).getText(), game, this); + // kicker variable X costs handled internally as multikicker with {1} cost (no multikicker on card) + if (!game.isSimulation()) { + game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue + " for " + " kicker X "); + } + ability.getManaCostsToPay().add(new GenericManaCost(xManaValue)); + } else { + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } + } else { + ability.getCosts().add(cost.copy()); + } + } + @Override public String getRule() { StringBuilder sb = new StringBuilder(); From 11ef6e87476f5c0657de28d34c5194c7b39686ac Mon Sep 17 00:00:00 2001 From: LoneFox Date: Wed, 22 Jul 2015 18:33:26 +0300 Subject: [PATCH 05/15] Implement cards: Canopy Surge, Divine Presence, Rewards of Diversity, and Tidal Visionary --- .../src/mage/sets/invasion/CanopySurge.java | 74 ++++++++++++ .../mage/sets/invasion/DivinePresence.java | 110 ++++++++++++++++++ .../sets/invasion/RewardsOfDiversity.java | 70 +++++++++++ .../mage/sets/invasion/TidalVisionary.java | 72 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/invasion/CanopySurge.java create mode 100644 Mage.Sets/src/mage/sets/invasion/DivinePresence.java create mode 100644 Mage.Sets/src/mage/sets/invasion/RewardsOfDiversity.java create mode 100644 Mage.Sets/src/mage/sets/invasion/TidalVisionary.java diff --git a/Mage.Sets/src/mage/sets/invasion/CanopySurge.java b/Mage.Sets/src/mage/sets/invasion/CanopySurge.java new file mode 100644 index 0000000000..60d7b425ba --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/CanopySurge.java @@ -0,0 +1,74 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.invasion; + +import java.util.UUID; +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DamageEverythingEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +/** + * + * @author LoneFox + */ +public class CanopySurge extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public CanopySurge(UUID ownerId) { + super(ownerId, 184, "Canopy Surge", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{G}"); + this.expansionSetCode = "INV"; + + // Kicker {2} + this.addAbility(new KickerAbility("{2}")); + // Canopy Surge deals 1 damage to each creature with flying and each player. If Canopy Surge was kicked, it deals 4 damage to each creature with flying and each player instead. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DamageEverythingEffect(4, filter), + new DamageEverythingEffect(1, filter), KickedCondition.getInstance(), + "{this} deals 1 damage to each creature with flying and each player. If {this} was kicked, it deals 4 damage to each creature with flying and each player instead.")); + } + + public CanopySurge(final CanopySurge card) { + super(card); + } + + @Override + public CanopySurge copy() { + return new CanopySurge(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/DivinePresence.java b/Mage.Sets/src/mage/sets/invasion/DivinePresence.java new file mode 100644 index 0000000000..99deb0039b --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/DivinePresence.java @@ -0,0 +1,110 @@ +/* + * 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.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +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.EventType; +import mage.game.events.GameEvent; + +/** + * + * @author LoneFox + */ +public class DivinePresence extends CardImpl { + + public DivinePresence(UUID ownerId) { + super(ownerId, 15, "Divine Presence", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + this.expansionSetCode = "INV"; + + // If a source would deal 4 or more damage to a creature or player, that source deals 3 damage to that creature or player instead. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DivinePresenceEffect())); + } + + public DivinePresence(final DivinePresence card) { + super(card); + } + + @Override + public DivinePresence copy() { + return new DivinePresence(this); + } +} + +class DivinePresenceEffect extends ReplacementEffectImpl { + + public DivinePresenceEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "If a source would deal 4 or more damage to a creature or player, that source deals 3 damage to that creature or player instead."; + } + + public DivinePresenceEffect(final DivinePresenceEffect effect) { + super(effect); + } + + @Override + public DivinePresenceEffect copy() { + return new DivinePresenceEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch(event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + return true; + default: + return false; + } + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getAmount() > 3; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(3); + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/RewardsOfDiversity.java b/Mage.Sets/src/mage/sets/invasion/RewardsOfDiversity.java new file mode 100644 index 0000000000..5505662f76 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/RewardsOfDiversity.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.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.MulticoloredPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; + +/** + * + * @author LoneFox + */ +public class RewardsOfDiversity extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("multicolored spell"); + + static { + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + filter.add(new MulticoloredPredicate()); + } + + public RewardsOfDiversity(UUID ownerId) { + super(ownerId, 32, "Rewards of Diversity", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + this.expansionSetCode = "INV"; + + // Whenever an opponent casts a multicolored spell, you gain 4 life. + this.addAbility(new SpellCastOpponentTriggeredAbility(new GainLifeEffect(4), filter, false)); + } + + public RewardsOfDiversity(final RewardsOfDiversity card) { + super(card); + } + + @Override + public RewardsOfDiversity copy() { + return new RewardsOfDiversity(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/TidalVisionary.java b/Mage.Sets/src/mage/sets/invasion/TidalVisionary.java new file mode 100644 index 0000000000..8bbbc694c7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/TidalVisionary.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.invasion; + +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.target.common.TargetCreaturePermanent; + +/** + * + * @author LoneFox + */ +public class TidalVisionary extends CardImpl { + + public TidalVisionary(UUID ownerId) { + super(ownerId, 80, "Tidal Visionary", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{U}"); + this.expansionSetCode = "INV"; + this.subtype.add("Merfolk"); + this.subtype.add("Wizard"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Target creature becomes the color of your choice until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(Duration.EndOfTurn), + new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public TidalVisionary(final TidalVisionary card) { + super(card); + } + + @Override + public TidalVisionary copy() { + return new TidalVisionary(this); + } +} From d9f88fdd6e215ab09121153b7db7d22a33bfe474 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 22 Jul 2015 21:02:04 +0200 Subject: [PATCH 06/15] * ChampionAbility - Fixed to work for all creatures. --- Mage/src/mage/abilities/keyword/ChampionAbility.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage/src/mage/abilities/keyword/ChampionAbility.java b/Mage/src/mage/abilities/keyword/ChampionAbility.java index 02e7151602..568a1ef946 100644 --- a/Mage/src/mage/abilities/keyword/ChampionAbility.java +++ b/Mage/src/mage/abilities/keyword/ChampionAbility.java @@ -91,7 +91,7 @@ public class ChampionAbility extends StaticAbility { this.subtypes = subtypes; StringBuilder sb = new StringBuilder("another "); ArrayList> subtypesPredicates = new ArrayList<>(); - if (subtypes != null) { + if (!subtypes[0].isEmpty()) { int i = 0; for (String subtype : this.subtypes) { subtypesPredicates.add(new SubtypePredicate(subtype)); @@ -107,7 +107,7 @@ public class ChampionAbility extends StaticAbility { } this.objectDescription = sb.toString(); FilterControlledPermanent filter = new FilterControlledPermanent(objectDescription); - if (subtypes != null) { + if (!subtypesPredicates.isEmpty()) { filter.add(Predicates.or(subtypesPredicates)); } filter.add(new AnotherPredicate()); From 4b7270672caf3edcacefa277b4df7c2f065cbf65 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 22 Jul 2015 21:46:32 +0200 Subject: [PATCH 07/15] Added Bane of the Living. --- .../mage/sets/legions/BaneOfTheLiving.java | 73 ++++++++++++++++++ .../abilities/common/TurnFaceUpAbility.java | 76 ++++++++++--------- .../common/ManacostVariableValue.java | 2 +- .../common/MorphManacostVariableValue.java | 42 ++++++++++ .../common/continuous/BoostAllEffect.java | 69 +++++++++-------- 5 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/legions/BaneOfTheLiving.java create mode 100644 Mage/src/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java diff --git a/Mage.Sets/src/mage/sets/legions/BaneOfTheLiving.java b/Mage.Sets/src/mage/sets/legions/BaneOfTheLiving.java new file mode 100644 index 0000000000..d78865f6a0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/legions/BaneOfTheLiving.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.legions; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.MorphManacostVariableValue; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class BaneOfTheLiving extends CardImpl { + + public BaneOfTheLiving(UUID ownerId) { + super(ownerId, 60, "Bane of the Living", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + this.expansionSetCode = "LGN"; + this.subtype.add("Insect"); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Morph {X}{B}{B} + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{X}{B}{B}"))); + // When Bane of the Living is turned face up, all creatures get -X/-X until end of turn. + DynamicValue morphX = new SignInversionDynamicValue(new MorphManacostVariableValue()); + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new BoostAllEffect(morphX, morphX, Duration.EndOfTurn, new FilterCreaturePermanent(), false, "", true))); + } + + public BaneOfTheLiving(final BaneOfTheLiving card) { + super(card); + } + + @Override + public BaneOfTheLiving copy() { + return new BaneOfTheLiving(this); + } +} diff --git a/Mage/src/mage/abilities/common/TurnFaceUpAbility.java b/Mage/src/mage/abilities/common/TurnFaceUpAbility.java index 0b6b9e0a0d..f08922dbab 100644 --- a/Mage/src/mage/abilities/common/TurnFaceUpAbility.java +++ b/Mage/src/mage/abilities/common/TurnFaceUpAbility.java @@ -1,38 +1,37 @@ /* -* 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.abilities.common; import mage.abilities.Ability; -import mage.abilities.ActivatedAbilityImpl; import mage.abilities.SpecialAction; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; +import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.constants.AbilityType; @@ -47,17 +46,23 @@ import mage.players.Player; * * @author LevelX2 */ - -public class TurnFaceUpAbility extends SpecialAction { +public class TurnFaceUpAbility extends SpecialAction { public TurnFaceUpAbility(Costs costs) { this(costs, false); } - + public TurnFaceUpAbility(Costs costs, boolean megamorph) { super(Zone.BATTLEFIELD); this.addEffect(new TurnFaceUpEffect(megamorph)); - this.addCost(costs); + for (Cost cost : costs) { + if (cost instanceof ManaCost) { + this.addManaCost((ManaCost) cost); + } else { + this.addCost(cost); + } + } + this.usesStack = false; this.abilityType = AbilityType.SPECIAL_ACTION; this.setRuleVisible(false); // will be made visible only to controller in CardView @@ -76,10 +81,10 @@ public class TurnFaceUpAbility extends SpecialAction { class TurnFaceUpEffect extends OneShotEffect { private final boolean megamorph; - + public TurnFaceUpEffect(boolean megamorph) { super(Outcome.Benefit); - this.staticText = "Turn this face-down permanent face up" +(megamorph ? " and put a +1/+1 counter on it":""); + this.staticText = "Turn this face-down permanent face up" + (megamorph ? " and put a +1/+1 counter on it" : ""); this.megamorph = megamorph; } @@ -97,13 +102,14 @@ class TurnFaceUpEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (controller != null && card !=null) { + if (controller != null && card != null) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { if (sourcePermanent.turnFaceUp(game, source.getControllerId())) { if (megamorph) { sourcePermanent.addCounters(CounterType.P1P1.createInstance(), game); } + game.getState().setValue(source.getSourceId().toString() + "TurnFaceUpX", source.getManaCostsToPay().getX()); return true; } } diff --git a/Mage/src/mage/abilities/dynamicvalue/common/ManacostVariableValue.java b/Mage/src/mage/abilities/dynamicvalue/common/ManacostVariableValue.java index 38b6968264..b6e7e2d603 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/ManacostVariableValue.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/ManacostVariableValue.java @@ -13,7 +13,7 @@ public class ManacostVariableValue implements DynamicValue { } @Override - public DynamicValue copy() { + public ManacostVariableValue copy() { return new ManacostVariableValue(); } diff --git a/Mage/src/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java b/Mage/src/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java new file mode 100644 index 0000000000..44c85ea4fa --- /dev/null +++ b/Mage/src/mage/abilities/dynamicvalue/common/MorphManacostVariableValue.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class MorphManacostVariableValue implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Integer xValue = (Integer) game.getState().getValue(sourceAbility.getSourceId().toString() + "TurnFaceUpX"); + if (xValue instanceof Integer) { + return xValue; + } + return 0; + } + + @Override + public MorphManacostVariableValue copy() { + return new MorphManacostVariableValue(); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/BoostAllEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BoostAllEffect.java index d050cfe0f5..70622957ce 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BoostAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BoostAllEffect.java @@ -1,43 +1,42 @@ /* -* 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.abilities.effects.common.continuous; import java.util.Iterator; import mage.MageObjectReference; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -84,9 +83,9 @@ public class BoostAllEffect extends ContinuousEffectImpl { this.toughness = toughness; this.filter = filter; this.excludeSource = excludeSource; - + this.lockedInPT = lockedInPT; - if (rule == null) { + if (rule == null || rule.isEmpty()) { setText(); } else { this.staticText = rule; @@ -111,7 +110,7 @@ public class BoostAllEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); if (this.affectedObjectsSet) { - for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (!(excludeSource && perm.getId().equals(source.getSourceId()))) { affectedObjectList.add(new MageObjectReference(perm, game)); } @@ -143,7 +142,7 @@ public class BoostAllEffect extends ContinuousEffectImpl { } } - } + } return true; } From 0e0678f9d1f35f270ea0b6a4be5859cb41661231 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 00:23:00 +0200 Subject: [PATCH 08/15] Added workaround to random booster drafr booster generation to prevent endless loops (#1136). --- Mage/src/mage/cards/ExpansionSet.java | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java index a7b968c724..5c30748055 100644 --- a/Mage/src/mage/cards/ExpansionSet.java +++ b/Mage/src/mage/cards/ExpansionSet.java @@ -120,8 +120,13 @@ public abstract class ExpansionSet implements Serializable { List theBooster = this.createBooster(); List commons = getCardsByRarity(Rarity.COMMON); - while (15 > theBooster.size()) { + int iterations = 0; + while (15 > theBooster.size() && !commons.isEmpty()) { addToBooster(theBooster, commons); + iterations++; + if (iterations > 14) { + break; + } } while (theBooster.size() > 15) { @@ -131,6 +136,18 @@ public abstract class ExpansionSet implements Serializable { return theBooster; } + protected void addToBooster(List booster, List cards) { + if (!cards.isEmpty()) { + CardInfo cardInfo = cards.remove(rnd.nextInt(cards.size())); + if (cardInfo != null) { + Card card = cardInfo.getCard(); + if (card != null) { + booster.add(card); + } + } + } + } + public List createBooster() { List booster = new ArrayList<>(); if (!hasBoosters) { @@ -257,18 +274,6 @@ public abstract class ExpansionSet implements Serializable { } } - protected void addToBooster(List booster, List cards) { - if (!cards.isEmpty()) { - CardInfo cardInfo = cards.remove(rnd.nextInt(cards.size())); - if (cardInfo != null) { - Card card = cardInfo.getCard(); - if (card != null) { - booster.add(card); - } - } - } - } - public boolean hasBoosters() { return hasBoosters; } From f77fd14b1effdf59aca76d3430c5f79902c517d4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 01:04:46 +0200 Subject: [PATCH 09/15] Added Cyclical Evolution. --- .../sets/futuresight/CyclicalEvolution.java | 75 +++++++++++++++++++ .../mage/sets/magicorigins/ElementalBond.java | 5 +- 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/futuresight/CyclicalEvolution.java diff --git a/Mage.Sets/src/mage/sets/futuresight/CyclicalEvolution.java b/Mage.Sets/src/mage/sets/futuresight/CyclicalEvolution.java new file mode 100644 index 0000000000..45bcce8946 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/CyclicalEvolution.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.futuresight; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SuspendAbility; +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; + +/** + * + * @author LevelX2 + */ +public class CyclicalEvolution extends CardImpl { + + public CyclicalEvolution(UUID ownerId) { + super(ownerId, 125, "Cyclical Evolution", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); + this.expansionSetCode = "FUT"; + + // Target creature gets +3/+3 until end of turn. Exile Cyclical Evolution with three time counters on it. + getSpellAbility().addEffect(new BoostTargetEffect(3, 3, Duration.EndOfTurn)); + getSpellAbility().addTarget(new TargetCreaturePermanent()); + getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), new StaticValue(3), true, true); + effect.setText("with 3 time counters on it"); + getSpellAbility().addEffect(effect); + + // Suspend 3-{2}{G} + this.addAbility(new SuspendAbility(3, new ManaCostsImpl<>("{2}{G}"), this)); + } + + public CyclicalEvolution(final CyclicalEvolution card) { + super(card); + } + + @Override + public CyclicalEvolution copy() { + return new CyclicalEvolution(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magicorigins/ElementalBond.java b/Mage.Sets/src/mage/sets/magicorigins/ElementalBond.java index 4c0b67a0c3..c0f1d5fbbd 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/ElementalBond.java +++ b/Mage.Sets/src/mage/sets/magicorigins/ElementalBond.java @@ -43,8 +43,9 @@ import mage.filter.predicate.mageobject.PowerPredicate; * @author emerald000 */ public class ElementalBond extends CardImpl { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creature with power 3 or greater"); + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("a creature with power 3 or greater"); + static { filter.add(new PowerPredicate(ComparisonType.GreaterThan, 2)); } From 363915075cf0129d602b2d9f7f31b2364c2d8f05 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 12:39:28 +0200 Subject: [PATCH 10/15] * City of Shadows - Fixed that the first ability was missing. --- Mage.Sets/src/mage/sets/thedark/CityOfShadows.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java b/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java index 6e4381050e..8651daa4ad 100644 --- a/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java +++ b/Mage.Sets/src/mage/sets/thedark/CityOfShadows.java @@ -45,7 +45,7 @@ import mage.target.common.TargetControlledCreaturePermanent; /** * - * @author anonymous + * @author Luna Skyrise */ public class CityOfShadows extends CardImpl { @@ -53,12 +53,14 @@ public class CityOfShadows extends CardImpl { super(ownerId, 113, "City of Shadows", Rarity.RARE, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "DRK"; - // {tap}, Exile a creature you control: Put a storage counter on City of Shadows. + // {T}, Exile a creature you control: Put a storage counter on City of Shadows. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new TapSourceCost()); ability.addCost(new ExileTargetCost(new TargetControlledCreaturePermanent())); - // {tap}: Add {X} to your mana pool, where X is the number of storage counters on City of Shadows. + this.addAbility(ability); + + // {T}: Add {X} to your mana pool, where X is the number of storage counters on City of Shadows. ability = new DynamicManaAbility(Mana.ColorlessMana, new CountersCount(CounterType.STORAGE), - "{tap}: Add {X} to your mana pool, where X is the number of storage counters on City of Shadows"); + "{tap}: Add {X} to your mana pool, where X is the number of storage counters on {this}"); this.addAbility(ability); } From b169e7e6c7c2d56ca2e4226aa97d1fc51f13266b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 12:42:02 +0200 Subject: [PATCH 11/15] * Mage-Ring Network - Fixed calculation of available mana. --- .../sets/magicorigins/MageRingNetwork.java | 20 ++++++---- .../org/mage/test/utils/ManaOptionsTest.java | 38 +++++++++++++++++++ Mage/src/mage/Mana.java | 11 +++--- .../dynamicvalue/common/CountersCount.java | 3 +- .../effects/common/DynamicManaEffect.java | 37 +++++++++++++----- .../abilities/mana/DynamicManaAbility.java | 24 ++++++++++-- Mage/src/mage/abilities/mana/ManaOptions.java | 2 +- Mage/src/mage/players/PlayerImpl.java | 6 +-- 8 files changed, 110 insertions(+), 31 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magicorigins/MageRingNetwork.java b/Mage.Sets/src/mage/sets/magicorigins/MageRingNetwork.java index 6ee273ea26..fe953e08e6 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/MageRingNetwork.java +++ b/Mage.Sets/src/mage/sets/magicorigins/MageRingNetwork.java @@ -34,6 +34,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CountersCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; @@ -47,7 +48,7 @@ import mage.counters.CounterType; /** * * @author LoneFox - + * */ public class MageRingNetwork extends CardImpl { @@ -55,17 +56,22 @@ public class MageRingNetwork extends CardImpl { super(ownerId, 249, "Mage-Ring Network", Rarity.UNCOMMON, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "ORI"; - // {t}: Add {1} to your mana pool. + // {T}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); - // {1}, {t}: Put a storage counter on Mage-Ring Network. + // {1}, {T}: Put a storage counter on Mage-Ring Network. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), - new ManaCostsImpl("{1}")); + new ManaCostsImpl("{1}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); - // {t}, Remove X storage counters from Mage-Ring Network: Add {x} to your mana pool. - ability = new DynamicManaAbility(Mana.ColorlessMana, new RemovedCountersForCostValue(), "Add {X} to your mana pool"); + // {T}, Remove X storage counters from Mage-Ring Network: Add {x} to your mana pool. + ability = new DynamicManaAbility( + Mana.ColorlessMana, + new RemovedCountersForCostValue(), + new TapSourceCost(), + "Add {X} to your mana pool", + true, new CountersCount(CounterType.STORAGE)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance(), - "Remove X storage counters from {this}")); + "Remove X storage counters from {this}")); this.addAbility(ability); } diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java index b4b40dd99e..2f0af102cb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java @@ -30,6 +30,7 @@ package org.mage.test.utils; import mage.abilities.mana.ManaOptions; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.counters.CounterType; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @@ -290,6 +291,43 @@ public class ManaOptionsTest extends CardTestPlayerBase { Assert.assertEquals("{B}{B}{B}", getManaOption(1, manaOptions)); } + @Test + public void testMageRingNetwork() { + // {T}: Add {1} to your mana pool. + // {T}, {1} : Put a storage counter on Mage-Ring Network. + // {T}, Remove X storage counters from Mage-Ring Network: Add {X} to your mana pool. + addCard(Zone.BATTLEFIELD, playerA, "Mage-Ring Network", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + setStopAt(1, PhaseStep.UPKEEP); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + Assert.assertEquals("{1}{W}{B}", getManaOption(0, manaOptions)); + } + + @Test + public void testMageRingNetwork2() { + // {T}: Add {1} to your mana pool. + // {T}, {1} : Put a storage counter on Mage-Ring Network. + // {T}, Remove X storage counters from Mage-Ring Network: Add {X} to your mana pool. + addCard(Zone.BATTLEFIELD, playerA, "Mage-Ring Network", 1); + addCounters(1, PhaseStep.UPKEEP, playerA, "Mage-Ring Network", CounterType.STORAGE, 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + setStopAt(1, PhaseStep.DRAW); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + Assert.assertEquals("{4}{W}{B}", getManaOption(0, manaOptions)); + } + @Test @Ignore // TriggeredManaAbilities not supported yet for getAvailableMana public void testCryptGhast() { diff --git a/Mage/src/mage/Mana.java b/Mage/src/mage/Mana.java index e24700d45d..b9b3e04c8f 100644 --- a/Mage/src/mage/Mana.java +++ b/Mage/src/mage/Mana.java @@ -597,10 +597,11 @@ public class Mana implements Comparable, Serializable, Copyable { } /** - * Returns the mana that is more colored but does not contain one less mana - * in any color but colorless if you call with {1}{W}{R} and {G}{W}{R} you - * get back {G}{W}{R} if you call with {G}{W}{R} and {G}{W}{R} you get back - * {G}{W}{R} if you call with {G}{W}{B} and {G}{W}{R} you get back null + * Returns the mana that is more colored or has a greater amount but does + * not contain one less mana in any color but colorless if you call with + * {1}{W}{R} and {G}{W}{R} you get back {G}{W}{R} if you call with {G}{W}{R} + * and {G}{W}{R} you get back {G}{W}{R} if you call with {G}{W}{B} and + * {G}{W}{R} you get back null * * @param mana1 * @param mana2 @@ -609,7 +610,7 @@ public class Mana implements Comparable, Serializable, Copyable { public static Mana getMoreValuableMana(Mana mana1, Mana mana2) { Mana moreMana; Mana lessMana; - if (mana2.count() > mana1.count() || mana2.getAny() > mana1.getAny() || mana2.getColorless() < mana1.getColorless()) { + if (mana2.countColored() > mana1.countColored() || mana2.getAny() > mana1.getAny() || mana2.count() > mana1.count()) { moreMana = mana2; lessMana = mana1; } else { diff --git a/Mage/src/mage/abilities/dynamicvalue/common/CountersCount.java b/Mage/src/mage/abilities/dynamicvalue/common/CountersCount.java index fd0e392619..9f6a5312a6 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/CountersCount.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/CountersCount.java @@ -1,15 +1,14 @@ package mage.abilities.dynamicvalue.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; public class CountersCount implements DynamicValue { + private final CounterType counter; public CountersCount(CounterType counter) { diff --git a/Mage/src/mage/abilities/effects/common/DynamicManaEffect.java b/Mage/src/mage/abilities/effects/common/DynamicManaEffect.java index 7747f36075..80df2007eb 100644 --- a/Mage/src/mage/abilities/effects/common/DynamicManaEffect.java +++ b/Mage/src/mage/abilities/effects/common/DynamicManaEffect.java @@ -44,32 +44,39 @@ public class DynamicManaEffect extends BasicManaEffect { private final Mana computedMana; private final DynamicValue amount; + private final DynamicValue netAmount; private String text = null; private boolean oneChoice; public DynamicManaEffect(Mana mana, DynamicValue amount) { - super(mana); - this.amount = amount; - computedMana = new Mana(); + this(mana, amount, null); } public DynamicManaEffect(Mana mana, DynamicValue amount, String text) { this(mana, amount, text, false); } + public DynamicManaEffect(Mana mana, DynamicValue amount, String text, boolean oneChoice) { + this(mana, amount, text, oneChoice, null); + } + /** * * @param mana * @param amount * @param text - * @param oneChoice is all mana from the same colour or if false the player can choose different colours + * @param oneChoice is all mana from the same colour or if false the player + * can choose different colours + * @param netAmount a dynamic value that calculates the possible available + * mana (e.g. if you have to pay by removing counters from source) */ - public DynamicManaEffect(Mana mana, DynamicValue amount, String text, boolean oneChoice) { + public DynamicManaEffect(Mana mana, DynamicValue amount, String text, boolean oneChoice, DynamicValue netAmount) { super(mana); this.amount = amount; computedMana = new Mana(); this.text = text; this.oneChoice = oneChoice; + this.netAmount = netAmount; } public DynamicManaEffect(final DynamicManaEffect effect) { @@ -78,6 +85,11 @@ public class DynamicManaEffect extends BasicManaEffect { this.amount = effect.amount.copy(); this.text = effect.text; this.oneChoice = effect.oneChoice; + if (effect.netAmount != null) { + this.netAmount = effect.netAmount.copy(); + } else { + this.netAmount = null; + } } @Override @@ -106,9 +118,16 @@ public class DynamicManaEffect extends BasicManaEffect { return null; } - public Mana computeMana(boolean netMana ,Game game, Ability source){ + public Mana computeMana(boolean netMana, Game game, Ability source) { this.computedMana.clear(); - int count = amount.calculate(game, source, this); + int count; + if (netMana && netAmount != null) { + // calculate the maximum available mana + count = netAmount.calculate(game, source, this); + } else { + count = amount.calculate(game, source, this); + } + if (mana.getBlack() > 0) { computedMana.setBlack(count); } else if (mana.getBlue() > 0) { @@ -126,7 +145,7 @@ public class DynamicManaEffect extends BasicManaEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { ChoiceColor choiceColor = new ChoiceColor(); - for(int i = 0; i < count; i++){ + for (int i = 0; i < count; i++) { if (!choiceColor.isChosen()) { while (!controller.choose(Outcome.Benefit, choiceColor, game)) { if (!controller.isInGame()) { @@ -150,7 +169,7 @@ public class DynamicManaEffect extends BasicManaEffect { } } } - } + } } else { computedMana.setColorless(count); } diff --git a/Mage/src/mage/abilities/mana/DynamicManaAbility.java b/Mage/src/mage/abilities/mana/DynamicManaAbility.java index 72336b74e0..15a44b71c3 100644 --- a/Mage/src/mage/abilities/mana/DynamicManaAbility.java +++ b/Mage/src/mage/abilities/mana/DynamicManaAbility.java @@ -48,6 +48,7 @@ public class DynamicManaAbility extends ManaAbility { /** * TapSourceCost added by default + * * @param mana * @param amount */ @@ -74,10 +75,24 @@ public class DynamicManaAbility extends ManaAbility { } public DynamicManaAbility(Mana mana, DynamicValue amount, Cost cost, String text, boolean oneChoice) { - super(Zone.BATTLEFIELD, new DynamicManaEffect(mana, amount, text, oneChoice), cost); - manaEffect = (DynamicManaEffect) this.getEffects().get(0); + this(mana, amount, cost, text, oneChoice, null); } + /** + * + * @param mana + * @param amount + * @param cost + * @param text + * @param oneChoice is all mana from the same colour or if false the player + * can choose different colours + * @param netAmount a dynamic value that calculates the possible available + * mana (e.g. if you have to pay by removing counters from source) + */ + public DynamicManaAbility(Mana mana, DynamicValue amount, Cost cost, String text, boolean oneChoice, DynamicValue netAmount) { + super(Zone.BATTLEFIELD, new DynamicManaEffect(mana, amount, text, oneChoice, netAmount), cost); + manaEffect = (DynamicManaEffect) this.getEffects().get(0); + } public DynamicManaAbility(final DynamicManaAbility ability) { super(ability); @@ -95,8 +110,9 @@ public class DynamicManaAbility extends ManaAbility { List newNetMana = new ArrayList<>(); if (game != null) { // TODO: effects from replacement effects like Mana Reflection are not considered yet + // TODO: effects that need a X payment (e.g. Mage-Ring Network) return always 0 newNetMana.add(manaEffect.computeMana(true, game, this)); - } - return newNetMana; + } + return newNetMana; } } diff --git a/Mage/src/mage/abilities/mana/ManaOptions.java b/Mage/src/mage/abilities/mana/ManaOptions.java index 4965a650b1..bb5977e813 100644 --- a/Mage/src/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/mage/abilities/mana/ManaOptions.java @@ -95,7 +95,7 @@ public class ManaOptions extends ArrayList { Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); if (moreValuable != null) { // only keep the more valuable mana - existingMana.setToMana(newMana); + existingMana.setToMana(moreValuable); continue SkipAddMana; } } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index a285cde583..0bc26e40f1 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2164,7 +2164,7 @@ public abstract class PlayerImpl implements Player, Serializable { public ManaOptions getManaAvailable(Game game) { ManaOptions available = new ManaOptions(); - List> sourceWithoutCosts = new ArrayList<>(); + List> sourceWithoutManaCosts = new ArrayList<>(); List> sourceWithCosts = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { boolean canAdd = false; @@ -2183,12 +2183,12 @@ public abstract class PlayerImpl implements Player, Serializable { if (withCost) { sourceWithCosts.add(manaAbilities); } else { - sourceWithoutCosts.add(manaAbilities); + sourceWithoutManaCosts.add(manaAbilities); } } } - for (Abilities manaAbilities : sourceWithoutCosts) { + for (Abilities manaAbilities : sourceWithoutManaCosts) { available.addMana(manaAbilities, game); } for (Abilities manaAbilities : sourceWithCosts) { From 7d3ff0551dfb1a7124c5b80d7f1386a72abdbff6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 15:58:10 +0200 Subject: [PATCH 12/15] Added a test. --- .../sets/magicorigins/NissaSageAnimist.java | 1 - .../sets/zendikar/QuestForTheGravelord.java | 4 +- .../test/cards/planeswalker/UginTest.java | 91 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 3 + .../base/impl/CardTestPlayerAPIImpl.java | 13 ++- Mage/src/mage/game/GameImpl.java | 15 ++- 6 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java index 6f256623cd..7959cf7f6b 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java +++ b/Mage.Sets/src/mage/sets/magicorigins/NissaSageAnimist.java @@ -67,7 +67,6 @@ public class NissaSageAnimist extends CardImpl { this.color.setGreen(true); this.nightCard = true; - this.canTransform = true; this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); diff --git a/Mage.Sets/src/mage/sets/zendikar/QuestForTheGravelord.java b/Mage.Sets/src/mage/sets/zendikar/QuestForTheGravelord.java index 540570e966..ddba7cf5ce 100644 --- a/Mage.Sets/src/mage/sets/zendikar/QuestForTheGravelord.java +++ b/Mage.Sets/src/mage/sets/zendikar/QuestForTheGravelord.java @@ -32,7 +32,6 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; @@ -53,7 +52,6 @@ public class QuestForTheGravelord extends CardImpl { super(ownerId, 108, "Quest for the Gravelord", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{B}"); this.expansionSetCode = "ZEN"; - // Whenever a creature dies, you may put a quest counter on Quest for the Gravelord. this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.QUEST.createInstance()), true)); // Remove three quest counters from Quest for the Gravelord and sacrifice it: Put a 5/5 black Zombie Giant creature token onto the battlefield. @@ -86,4 +84,4 @@ class ZombieToken extends Token { power = new MageInt(5); toughness = new MageInt(5); } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java new file mode 100644 index 0000000000..bc396fbfd0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/UginTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.planeswalker; + +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 UginTest extends CardTestPlayerBase { + + @Test + public void testCard() { + // +2: Ugin, the Spirit Dragon deals 3 damage to target creature or player. + // -X: Exile each permanent with converted mana cost X or less that's one or more colors. + // -10: You gain 7 life, draw 7 cards, then put up to seven permanent cards from your hand onto the battlefield. + addCard(Zone.BATTLEFIELD, playerA, "Ugin, the Spirit Dragon"); // starts with 7 Loyality counters + // Whenever a creature dies, you may put a quest counter on Quest for the Gravelord. + addCard(Zone.BATTLEFIELD, playerA, "Quest for the Gravelord"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + addCard(Zone.LIBRARY, playerB, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 6); + // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library. + // Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control. + // +1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand. + // -2: Put a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World onto the battlefield. + // -7: Untap up to six target lands. They become 6/6 Elemental creatures. They're still lands. + addCard(Zone.HAND, playerB, "Nissa, Vastwood Seer"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {source} deals 3 damage to target creature or player.", playerB); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nissa, Vastwood Seer"); + playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Forest"); + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "-2: Put a legendary 4/4 green Elemental creature token named Ashaya, the Awoken World onto the battlefield."); + + attack(3, playerA, "Silvercoat Lion"); + block(3, playerB, "Ashaya, the Awoken World", "Silvercoat Lion"); + + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "-X: Exile each permanent with converted mana cost X or less that's one or more colors"); + setChoice(playerA, "X=0"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Ugin, the Spirit Dragon", 1); + assertCounterCount("Ugin, the Spirit Dragon", CounterType.LOYALTY, 9); // 7 + 2 - 0 + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Ashaya, the Awoken World", 0); + + assertExileCount("Nissa, Vastwood Seer", 1); + + assertCounterCount("Quest for the Gravelord", CounterType.QUEST, 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 683c11da3e..4b8baa125c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -253,6 +253,9 @@ public class TestPlayer implements Player { int targetsSet = 0; for (Player player : game.getPlayers().values()) { if (player.getName().equals(target)) { + if (ability.getTargets().isEmpty()) { + throw new UnsupportedOperationException("Ability has no targets, but there is a player target set - " + ability.toString()); + } ability.getTargets().get(0).addTarget(player.getId(), ability, game); targetsSet++; break; diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 25b40da186..d40b5113bf 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -219,6 +219,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * * @param player {@link Player} to remove all library cards from. */ + @Override public void removeAllCardsFromLibrary(TestPlayer player) { getCommands(player).put(Zone.LIBRARY, "clear"); } @@ -241,6 +242,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * playerB. * @param cardName Card name in string format. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName) { addCard(gameZone, player, cardName, 1, false); } @@ -254,6 +256,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param cardName Card name in string format. * @param count Amount of cards to be added. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); } @@ -270,6 +273,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * permanent should be tapped. In case gameZone is other than Battlefield, * {@link IllegalArgumentException} is thrown */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { if (gameZone.equals(Zone.BATTLEFIELD)) { @@ -324,6 +328,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player {@link Player} to set life count for. * @param life Life count to set. */ + @Override public void setLife(TestPlayer player, int life) { getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life)); } @@ -714,16 +719,16 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert whether X permanents of the same name are tapped or not. * - * @param cardName Name of the permanent that should be checked. - * @param tapped Whether the permanent is tapped or not - * @param count The amount of this permanents that should be tapped + * @param cardName Name of the permanent that should be checked. + * @param tapped Whether the permanent is tapped or not + * @param count The amount of this permanents that should be tapped */ public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError { int tappedAmount = 0; Permanent found = null; for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) { if (permanent.getName().equals(cardName)) { - if(permanent.isTapped() == tapped) { + if (permanent.isTapped() == tapped) { tappedAmount++; } found = permanent; diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 07fb43d580..b14cd2908a 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -44,6 +44,7 @@ import java.util.Random; import java.util.Set; import java.util.Stack; import java.util.UUID; +import mage.MageException; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; @@ -1173,6 +1174,7 @@ public abstract class GameImpl implements Game, Serializable { @Override public void playPriority(UUID activePlayerId, boolean resuming) { + int errorContinueCounter = 0; int bookmark = 0; clearAllBookmarks(); try { @@ -1239,11 +1241,19 @@ public abstract class GameImpl implements Game, Serializable { } } catch (Exception ex) { logger.fatal("Game exception gameId: " + getId(), ex); - ex.printStackTrace(); this.fireErrorEvent("Game exception occurred: ", ex); restoreState(bookmark, ""); bookmark = 0; - continue; + Player activePlayer = this.getPlayer(getActivePlayerId()); + if (errorContinueCounter > 15) { + throw new MageException("Iterated player priority after game exception too often, game ends!"); + } + if (activePlayer != null && !activePlayer.isTestMode()) { + errorContinueCounter++; + continue; + } else { + throw new MageException("Error in testclass"); + } } state.getPlayerList().getNext(); } @@ -1251,6 +1261,7 @@ public abstract class GameImpl implements Game, Serializable { } catch (Exception ex) { logger.fatal("Game exception ", ex); this.fireErrorEvent("Game exception occurred: ", ex); + this.end(); } finally { resetLKI(); clearAllBookmarks(); From 2e01d2ec52abbaaeefa25d489834d8aeb27715ae Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 16:00:15 +0200 Subject: [PATCH 13/15] * Swift Warkite - Fixed that it returned object also if the object changed zone meanwhile. --- .../sets/dragonsoftarkir/SwiftWarkite.java | 8 ++--- .../sets/returntoravnica/GraveBetrayal.java | 35 +++++++------------ .../sets/scarsofmirrodin/ArgentSphinx.java | 24 +++++++------ .../scarsofmirrodin/VenserTheSojourner.java | 8 ++--- 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java index d3a8b1757e..7cf4b87280 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/SwiftWarkite.java @@ -126,10 +126,10 @@ class SwiftWarkiteEffect extends OneShotEffect { Permanent creature = game.getPermanent(card.getId()); if (creature != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game))); game.addEffect(effect, source); Effect effect2 = new ReturnToHandTargetEffect(); - effect2.setTargetPointer(new FixedTarget(creature.getId())); + effect2.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game))); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setSourceId(source.getSourceId()); @@ -147,10 +147,10 @@ class SwiftWarkiteEffect extends OneShotEffect { Permanent creature = game.getPermanent(card.getId()); if (creature != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game))); game.addEffect(effect, source); Effect effect2 = new ReturnToHandTargetEffect(); - effect2.setTargetPointer(new FixedTarget(creature.getId())); + effect2.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game))); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect2); delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setSourceId(source.getSourceId()); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java index d28418b909..dab0ee466f 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.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.sets.returntoravnica; import java.util.UUID; @@ -58,21 +57,19 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class GraveBetrayal extends CardImpl { - public GraveBetrayal (UUID ownerId) { + public GraveBetrayal(UUID ownerId) { super(ownerId, 67, "Grave Betrayal", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{5}{B}{B}"); this.expansionSetCode = "RTR"; - // Whenever a creature you don't control dies, return it to the battlefield under // your control with an additional +1/+1 counter on it at the beginning of the // next end step. That creature is a black Zombie in addition to its other colors and types. this.addAbility(new GraveBetrayalTriggeredAbility()); } - public GraveBetrayal (final GraveBetrayal card) { + public GraveBetrayal(final GraveBetrayal card) { super(card); } @@ -108,13 +105,10 @@ class GraveBetrayalTriggeredAbility extends TriggeredAbilityImpl { && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && !permanent.getControllerId().equals(this.getControllerId()) && permanent.getCardType().contains(CardType.CREATURE)) { - Card card = (Card)game.getObject(permanent.getId()); + Card card = (Card) game.getObject(permanent.getId()); if (card != null) { Effect effect = new GraveBetrayalEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - Integer zoneChanges = card.getZoneChangeCounter(game); - effect.setValue("zoneChanges", zoneChanges); - + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); delayedAbility.setSourceId(this.getSourceId()); delayedAbility.setControllerId(this.getControllerId()); @@ -153,17 +147,14 @@ class GraveBetrayalEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(targetPointer.getFirst(game, source)); if (card != null) { - Integer zoneChanges = (Integer) getValue("zoneChanges"); - if (card.getZoneChangeCounter(game) == zoneChanges) { - Zone currentZone = game.getState().getZone(card.getId()); - if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), source.getControllerId())) { - Permanent creature = game.getPermanent(card.getId()); - creature.addCounters(CounterType.P1P1.createInstance(), game); - ContinuousEffect effect = new GraveBetrayalContiniousEffect(); - effect.setTargetPointer(new FixedTarget(creature.getId())); - game.addEffect(effect, source); - return true; - } + Zone currentZone = game.getState().getZone(card.getId()); + if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), source.getControllerId())) { + Permanent creature = game.getPermanent(card.getId()); + creature.addCounters(CounterType.P1P1.createInstance(), game); + ContinuousEffect effect = new GraveBetrayalContiniousEffect(); + effect.setTargetPointer(new FixedTarget(creature.getId())); + game.addEffect(effect, source); + return true; } } return false; @@ -220,4 +211,4 @@ class GraveBetrayalContiniousEffect extends ContinuousEffectImpl { return layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java index 3613ce6d0d..3863832e51 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentSphinx.java @@ -25,15 +25,11 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -43,7 +39,10 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.keyword.FlyingAbility; 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; @@ -53,7 +52,7 @@ import mage.game.permanent.Permanent; */ public class ArgentSphinx extends CardImpl { - public ArgentSphinx (UUID ownerId) { + public ArgentSphinx(UUID ownerId) { super(ownerId, 28, "Argent Sphinx", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); this.expansionSetCode = "SOM"; this.subtype.add("Sphinx"); @@ -66,7 +65,7 @@ public class ArgentSphinx extends CardImpl { this.addAbility(ability); } - public ArgentSphinx (final ArgentSphinx card) { + public ArgentSphinx(final ArgentSphinx card) { super(card); } @@ -81,7 +80,7 @@ class ArgentSphinxEffect extends OneShotEffect { private static final String effectText = "Exile {this}. Return it to the battlefield under your control at the beginning of the next end step"; - ArgentSphinxEffect ( ) { + ArgentSphinxEffect() { super(Outcome.Benefit); staticText = effectText; } @@ -93,10 +92,13 @@ class ArgentSphinxEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.moveToExile(source.getSourceId(), "Argent Sphinx Exile", source.getSourceId(), game)) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (permanent != null && sourceObject != null) { + if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game)) { //create delayed triggered ability - AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility + = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setSourceObject(source.getSourceObject(game), game); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java index 2182a3dd8a..2f4def3ecf 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/VenserTheSojourner.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.sets.scarsofmirrodin; import java.util.UUID; @@ -81,8 +80,6 @@ public class VenserTheSojourner extends CardImpl { this.expansionSetCode = "SOM"; this.subtype.add("Venser"); - - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); // +2: Exile target permanent you own. Return it to the battlefield under your control at the beginning of the next end step. @@ -131,7 +128,7 @@ class VenserTheSojournerEffect extends OneShotEffect { if (getTargetPointer().getFirst(game, source) != null) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { - if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourceObject.getName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { + if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { //create delayed triggered ability AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD)); delayedAbility.setSourceId(source.getSourceId()); @@ -174,7 +171,8 @@ class VenserTheSojournerSpellCastTriggeredAbility extends TriggeredAbilityImpl { protected FilterSpell filter; /** - * If true, the source that triggered the ability will be set as target to effect. + * If true, the source that triggered the ability will be set as target to + * effect. */ protected boolean rememberSource = false; From 05e35bcae77b2641f3e90629aa045bde8cdd8b1f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 23 Jul 2015 19:53:34 +0200 Subject: [PATCH 14/15] Fixed build problem with OptionalAdditionalCostImpl. --- .../costs/OptionalAdditionalCost.java | 2 +- .../costs/OptionalAdditionalCostImpl.java | 44 ++++++++++--------- .../mage/abilities/keyword/KickerAbility.java | 18 +++++--- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java b/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java index 949bbdfe45..83aa204299 100644 --- a/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java +++ b/Mage/src/mage/abilities/costs/OptionalAdditionalCost.java @@ -30,7 +30,7 @@ package mage.abilities.costs; /** * @author LevelX2 */ -public interface OptionalAdditionalCost extends Costs { +public interface OptionalAdditionalCost extends Cost { String getName(); diff --git a/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java b/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java index 6132df52fc..489d8690ea 100644 --- a/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java +++ b/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.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.abilities.costs; /** @@ -33,10 +32,7 @@ package mage.abilities.costs; * @author LevelX2 * @param */ - - -public class OptionalAdditionalCostImpl extends CostsImpl implements OptionalAdditionalCost{ - +public class OptionalAdditionalCostImpl extends CostsImpl implements OptionalAdditionalCost { protected String name; protected String reminderText; @@ -104,26 +100,26 @@ public class OptionalAdditionalCostImpl e } /** - * Returns a text suffix for the game log, that can be added to - * the cast message. + * Returns a text suffix for the game log, that can be added to the cast + * message. * - * @param position - if there are multiple costs, it's the postion the cost is set (starting with 0) + * @param position - if there are multiple costs, it's the postion the cost + * is set (starting with 0) * @return */ @Override public String getCastSuffixMessage(int position) { StringBuilder sb = new StringBuilder(); if (isActivated() && (!isRepeatable() || getActivateCount() > 0)) { - sb.append(position > 0 ? " and ":"").append(" with "); + sb.append(position > 0 ? " and " : "").append(" with "); if (isRepeatable()) { - sb.append(getActivateCount()).append(getActivateCount() > 1? " times ":" time "); + sb.append(getActivateCount()).append(getActivateCount() > 1 ? " times " : " time "); } sb.append(name); } - return sb.toString(); + return sb.toString(); } - /** * If the player intends to pay the cost, the cost will be activated * @@ -132,7 +128,9 @@ public class OptionalAdditionalCostImpl e public void activate() { activated = true; ++activatedCounter; - }; + } + + ; /** * Reset the activate and count information @@ -152,7 +150,7 @@ public class OptionalAdditionalCostImpl e @Override public void setRepeatable(boolean repeatable) { this.repeatable = repeatable; - } + } /** * Can the cost be multiple times activated @@ -162,7 +160,9 @@ public class OptionalAdditionalCostImpl e @Override public boolean isRepeatable() { return repeatable; - }; + } + + ; /** * Returns if the cost was activated @@ -170,20 +170,24 @@ public class OptionalAdditionalCostImpl e * @return */ @Override - public boolean isActivated(){ + public boolean isActivated() { return activated; - }; + } + + ; /** * Returns the number of times the cost was activated * @return */ @Override - public int getActivateCount(){ + public int getActivateCount() { return activatedCounter; - }; + } + + ; + - @Override public OptionalAdditionalCostImpl copy() { return new OptionalAdditionalCostImpl(this); diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index b262a3cc29..9056b7bc2e 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -218,15 +218,19 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo if (kickerCost.canPay(ability, sourceId, controllerId, game) && player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) { this.activateKicker(kickerCost, ability, game); - for (Iterator itKickerCost = kickerCost.iterator(); itKickerCost.hasNext();) { - Object kickerCostObject = itKickerCost.next(); - if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) { - for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) { - addKickerCostsToAbility(itDetails.next(), ability, game); + if (kickerCost instanceof Costs) { + for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) { + Object kickerCostObject = itKickerCost.next(); + if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) { + for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) { + addKickerCostsToAbility(itDetails.next(), ability, game); + } + } else { + addKickerCostsToAbility((Cost) kickerCostObject, ability, game); } - } else { - addKickerCostsToAbility((Cost) kickerCostObject, ability, game); } + } else { + addKickerCostsToAbility((Cost) kickerCost, ability, game); } again = kickerCost.isRepeatable(); } else { From 0f6fab52489b35442c5356e56bb40048b866f3db Mon Sep 17 00:00:00 2001 From: Simown Date: Wed, 22 Jul 2015 22:01:02 +0100 Subject: [PATCH 15/15] Small fixes to "Domain" cards Draco, Fettergeist and Samite Pilgrim. --- .../mage/sets/avacynrestored/Fettergeist.java | 5 +-- Mage.Sets/src/mage/sets/ftvdragons/Draco.java | 6 +-- .../mage/sets/planeshift/SamitePilgrim.java | 42 ++----------------- 3 files changed, 5 insertions(+), 48 deletions(-) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/Fettergeist.java b/Mage.Sets/src/mage/sets/avacynrestored/Fettergeist.java index 6b8cec80c8..cd1e936ab9 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/Fettergeist.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/Fettergeist.java @@ -102,10 +102,7 @@ class FettergeistUnlessPaysEffect extends OneShotEffect { if (player != null && permanent != null) { PermanentsOnBattlefieldCount amount = new PermanentsOnBattlefieldCount(filter, 1); int count = amount.calculate(game, source, this); - if (count == 0) { - return true; - } - if (player.chooseUse(Outcome.Benefit, "Pay " + count + "?", source, game)) { + if (player.chooseUse(Outcome.Benefit, "Pay " + count + "? Or " + permanent.getName() + " will be sacrificed.", source, game)) { GenericManaCost cost = new GenericManaCost(count); if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { return true; diff --git a/Mage.Sets/src/mage/sets/ftvdragons/Draco.java b/Mage.Sets/src/mage/sets/ftvdragons/Draco.java index e169224878..2335c0a2e9 100644 --- a/Mage.Sets/src/mage/sets/ftvdragons/Draco.java +++ b/Mage.Sets/src/mage/sets/ftvdragons/Draco.java @@ -124,11 +124,7 @@ class DracoSacrificeUnlessPaysEffect extends OneShotEffect { if (player != null && permanent != null) { // The cost is reduced by {2} for each basic land type. int domainValueReduction = new DomainValue(2).calculate(game, source, this); - // Nothing to pay - if (domainValueReduction >= MAX_DOMAIN_VALUE) { - return true; - } - int count = (MAX_DOMAIN_VALUE-domainValueReduction ); + int count = MAX_DOMAIN_VALUE - domainValueReduction; if (player.chooseUse(Outcome.Benefit, "Pay {" + count + "}? Or " + permanent.getName() + " will be sacrificed.", source, game)) { GenericManaCost cost = new GenericManaCost(count); if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { diff --git a/Mage.Sets/src/mage/sets/planeshift/SamitePilgrim.java b/Mage.Sets/src/mage/sets/planeshift/SamitePilgrim.java index c2fa8ef4db..81ea8fbc8e 100644 --- a/Mage.Sets/src/mage/sets/planeshift/SamitePilgrim.java +++ b/Mage.Sets/src/mage/sets/planeshift/SamitePilgrim.java @@ -55,7 +55,7 @@ public class SamitePilgrim extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Domain - {tap}: Prevent the next X damage that would be dealt to target creature this turn, where X is the number of basic land types among lands you control. + // Domain - {T}: Prevent the next X damage that would be dealt to target creature this turn, where X is the number of basic land types among lands you control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SamitePilgrimPreventDamageToTargetEffect(), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); @@ -73,16 +73,14 @@ public class SamitePilgrim extends CardImpl { class SamitePilgrimPreventDamageToTargetEffect extends PreventionEffectImpl { - protected int amount = 0; public SamitePilgrimPreventDamageToTargetEffect() { - super(Duration.EndOfTurn); + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, true); staticText = "Prevent the next X damage that would be dealt to target creature this turn, where X is the number of basic land types among lands you control."; } public SamitePilgrimPreventDamageToTargetEffect(final SamitePilgrimPreventDamageToTargetEffect effect) { super(effect); - this.amount = effect.amount; } @Override @@ -93,7 +91,7 @@ class SamitePilgrimPreventDamageToTargetEffect extends PreventionEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - amount = new DomainValue().calculate(game, source, this); + amountToPrevent = new DomainValue().calculate(game, source, this); } @Override @@ -101,40 +99,6 @@ class SamitePilgrimPreventDamageToTargetEffect extends PreventionEffectImpl { return true; } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - boolean result = false; - int toPrevent = amount; - if (event.getAmount() < this.amount) { - toPrevent = event.getAmount(); - amount -= event.getAmount(); - } else { - amount = 0; - } - GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, source.getControllerId(), source.getSourceId(), source.getControllerId(), toPrevent, false); - if (!game.replaceEvent(preventEvent)) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - if (amount == 0) { - this.used = true; - this.discard(); - } - if (event.getAmount() >= toPrevent) { - event.setAmount(event.getAmount() - toPrevent); - } else { - event.setAmount(0); - result = true; - } - if (toPrevent > 0) { - game.informPlayers(new StringBuilder("Samite Pilgrim ").append("prevented ").append(toPrevent).append(" to ").append(targetCreature.getName()).toString()); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, - source.getControllerId(), source.getSourceId(), source.getControllerId(), toPrevent)); - } - } - } - return result; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return !this.used && super.applies(event, source, game) && event.getTargetId().equals(source.getFirstTarget());