diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheFields.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheFields.java index 195aa4b03ea..80ed488f058 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheFields.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheFields.java @@ -35,15 +35,19 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.DealsDamageGainLifeSourceTriggeredAbility; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continious.BecomesCreatureAttachedEffect; +import mage.abilities.effects.common.continious.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; import mage.constants.Duration; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; @@ -79,7 +83,11 @@ public class GenjuOfTheFields extends CardImpl { this.addAbility(ability); // {2}: Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature with "Whenever this creature deals damage, its controller gains that much life." It's still a land. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureAttachedEffect(new SpiritToken(), "Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature with \"Whenever this creature deals damage, its controller gains that much life.\". It's still a land", Duration.EndOfTurn),new GenericManaCost(2)); + Effect effect = new BecomesCreatureAttachedEffect(new SpiritToken(), + "Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature", Duration.EndOfTurn); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect,new GenericManaCost(2)); + effect = new GainAbilityAttachedEffect(new DealsDamageGainLifeSourceTriggeredAbility(), AttachmentType.AURA, Duration.EndOfTurn); + effect.setText("with \"Whenever this creature deals damage, its controller gains that much life.\". It's still a land"); this.addAbility(ability2); // When enchanted Plains is put into a graveyard, you may return Genju of the Fields from your graveyard to your hand. @@ -98,13 +106,12 @@ public class GenjuOfTheFields extends CardImpl { private class SpiritToken extends Token { SpiritToken() { - super("Spirit", "2/5 white Spirit creature with with \"Whenever this creature deals damage, its controller gains that much life.\""); + super("Spirit", "2/5 white Spirit creature"); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add("Spirit"); power = new MageInt(2); toughness = new MageInt(5); - addAbility(LifelinkAbility.getInstance()); } } } diff --git a/Mage.Sets/src/mage/sets/guildpact/MourningThrull.java b/Mage.Sets/src/mage/sets/guildpact/MourningThrull.java index ae46f8b38da..5a88b44638c 100644 --- a/Mage.Sets/src/mage/sets/guildpact/MourningThrull.java +++ b/Mage.Sets/src/mage/sets/guildpact/MourningThrull.java @@ -29,19 +29,11 @@ package mage.sets.guildpact; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DealsDamageGainLifeSourceTriggeredAbility; 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.events.GameEvent; -import mage.players.Player; /** * @@ -62,7 +54,7 @@ public class MourningThrull extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Mourning Thrull deals damage, you gain that much life. - this.addAbility(new MourningThrullTriggeredAbility()); + this.addAbility(new DealsDamageGainLifeSourceTriggeredAbility()); } public MourningThrull(final MourningThrull card) { @@ -74,69 +66,3 @@ public class MourningThrull extends CardImpl { return new MourningThrull(this); } } - -class MourningThrullTriggeredAbility extends TriggeredAbilityImpl { - - public MourningThrullTriggeredAbility() { - super(Zone.BATTLEFIELD, new MourningThrullEffect(), false); - } - - public MourningThrullTriggeredAbility(final MourningThrullTriggeredAbility ability) { - super(ability); - } - - @Override - public MourningThrullTriggeredAbility copy() { - return new MourningThrullTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { - if (event.getSourceId().equals(this.getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} deals damage, " + super.getRule(); - } -} - -class MourningThrullEffect extends OneShotEffect { - - public MourningThrullEffect() { - super(Outcome.GainLife); - this.staticText = "you gain that much life"; - } - - public MourningThrullEffect(final MourningThrullEffect effect) { - super(effect); - } - - @Override - public MourningThrullEffect copy() { - return new MourningThrullEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.gainLife(amount, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/modernmasters/StirThePride.java b/Mage.Sets/src/mage/sets/modernmasters/StirThePride.java index ce6196b28af..61314a14c6c 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/StirThePride.java +++ b/Mage.Sets/src/mage/sets/modernmasters/StirThePride.java @@ -28,24 +28,16 @@ package mage.sets.modernmasters; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsDamageGainLifeSourceTriggeredAbility; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continious.BoostControlledEffect; import mage.abilities.effects.common.continious.GainAbilityControlledEffect; import mage.abilities.keyword.EntwineAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.players.Player; /** * @@ -66,7 +58,7 @@ public class StirThePride extends CardImpl { this.getSpellAbility().addEffect(new BoostControlledEffect(2,2, Duration.EndOfTurn)); // or until end of turn, creatures you control gain "Whenever this creature deals damage, you gain that much life." Mode mode = new Mode(); - Effect effect = new GainAbilityControlledEffect(new StirThePrideTriggeredAbility(), Duration.EndOfTurn); + Effect effect = new GainAbilityControlledEffect(new DealsDamageGainLifeSourceTriggeredAbility(), Duration.EndOfTurn); effect.setText("until end of turn, creatures you control gain \"Whenever this creature deals damage, you gain that much life.\""); mode.getEffects().add(effect); this.getSpellAbility().getModes().addMode(mode); @@ -85,71 +77,3 @@ public class StirThePride extends CardImpl { return new StirThePride(this); } } - -class StirThePrideTriggeredAbility extends TriggeredAbilityImpl { - - public StirThePrideTriggeredAbility() { - super(Zone.BATTLEFIELD, new StirThePrideGainLifeEffect(), false); - } - - public StirThePrideTriggeredAbility(final StirThePrideTriggeredAbility ability) { - super(ability); - } - - @Override - public StirThePrideTriggeredAbility copy() { - return new StirThePrideTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(EventType.DAMAGED_CREATURE) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { - if (event.getSourceId().equals(this.getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - } - return true; - } - - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} deals damage, " + super.getRule(); - } - -} - -class StirThePrideGainLifeEffect extends OneShotEffect { - - public StirThePrideGainLifeEffect() { - super(Outcome.GainLife); - this.staticText = "you gain that much life"; - } - - public StirThePrideGainLifeEffect(final StirThePrideGainLifeEffect effect) { - super(effect); - } - - @Override - public StirThePrideGainLifeEffect copy() { - return new StirThePrideGainLifeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.gainLife(amount, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/timeshifted/EssenceSliver.java b/Mage.Sets/src/mage/sets/timeshifted/EssenceSliver.java index 67a50bb5b53..bf6201e3c37 100644 --- a/Mage.Sets/src/mage/sets/timeshifted/EssenceSliver.java +++ b/Mage.Sets/src/mage/sets/timeshifted/EssenceSliver.java @@ -73,7 +73,7 @@ public class EssenceSliver extends CardImpl { class DealsDamageTriggeredAbility extends TriggeredAbilityImpl { - private boolean setTargetPointer; + private final boolean setTargetPointer; public DealsDamageTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { super(Zone.BATTLEFIELD, effect, optional); diff --git a/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java b/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java index bf973a06316..c85abe05d25 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java +++ b/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java @@ -34,11 +34,14 @@ import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continious.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.continious.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.GreenManaAbility; import mage.abilities.mana.RedManaAbility; @@ -55,10 +58,20 @@ public class RagingRavine extends CardImpl { public RagingRavine(UUID ownerId) { super(ownerId, 141, "Raging Ravine", Rarity.RARE, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "WWK"; + + // Raging Ravine enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); + // Tap: Add Red or Green to your mana pool. this.addAbility(new GreenManaAbility()); this.addAbility(new RedManaAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new RagingRavineToken(), "land", Duration.EndOfTurn), new ManaCostsImpl("{2}{R}{G}"))); + Effect effect = new BecomesCreatureSourceEffect(new RagingRavineToken(), "land", Duration.EndOfTurn); + effect.setText("Until end of turn, {this} becomes a 3/3 red and green Elemental creature"); + // {2}{R}{G}: Until end of turn, Raging Ravine becomes a 3/3 red and green Elemental creature with "Whenever this creature attacks, put a +1/+1 counter on it." It's still a land. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{2}{R}{G}")); + effect = new GainAbilitySourceEffect(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false), Duration.EndOfTurn); + effect.setText("with \"Whenever this creature attacks, put a +1/+1 counter on it.\" It's still a land"); + ability.addEffect(effect); + this.addAbility(ability); } public RagingRavine(final RagingRavine card) { @@ -75,13 +88,12 @@ public class RagingRavine extends CardImpl { class RagingRavineToken extends Token { public RagingRavineToken() { - super("", "3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\""); + super("", "3/3 red and green Elemental creature"); cardType.add(CardType.CREATURE); subtype.add("Elemental"); color.setRed(true); color.setGreen(true); power = new MageInt(3); - toughness = new MageInt(3); - addAbility(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false)); + toughness = new MageInt(3); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java new file mode 100644 index 00000000000..dfdeb66fa6e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class EvokeTest extends CardTestPlayerBase { + + /* + * Shriekmaw {4}{B} + * Creature — Elemental + * 3/2 + * Fear (This creature can't be blocked except by artifact creatures and/or black creatures.) + * When Shriekmaw enters the battlefield, destroy target nonartifact, nonblack creature. + * Evoke {1}{B} (You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield.) + + Exhume {1}{B} + Sorcery + Each player puts a creature card from his or her graveyard onto the battlefield. + + */ + + @Test + public void testCreatureComesIntoPlay() { + // Check that Lion goes to graveyard from evoke ability + // Check that evoke does not trigger again to sacrifice Shriekmaw if it's exhumed + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.HAND, playerA, "Shriekmaw"); + addCard(Zone.HAND, playerA, "Exhume"); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shriekmaw"); + setChoice(playerA, "Yes"); + addTarget(playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhume"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Exhume", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // went again to graveyard from shriekmaw's triggered ability + + assertPermanentCount(playerA, "Shriekmaw", 1); + } + + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java index 576289817ed..5da3f1b6fb4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/TurnToFrogTest.java @@ -23,7 +23,7 @@ public class TurnToFrogTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.HAND, playerB, "Turn to Frog"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land."); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn, {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" It's still a land."); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn to Frog", "Raging Ravine"); attack(1, playerA, "Raging Ravine"); @@ -44,10 +44,10 @@ public class TurnToFrogTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.HAND, playerB, "Turn to Frog"); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land."); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn, {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" It's still a land."); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn to Frog", "Raging Ravine"); - activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land."); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: Until end of turn, {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" It's still a land."); attack(3, playerA, "Raging Ravine"); setStopAt(3, PhaseStep.END_COMBAT); 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 3e65d839195..4ece5b331cc 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 @@ -360,7 +360,7 @@ public class TestPlayer extends ComputerPlayer { } } else { if (ability.getTargets().size() == 0) { - throw new AssertionError("Ability has no targets."); + throw new AssertionError("Ability has no targets. " + ability.toString()); } for (UUID id: ability.getTargets().get(0).possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { MageObject object = game.getObject(id); diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index c4805c97834..3ee2235b124 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -920,8 +920,8 @@ public abstract class AbilityImpl> implements Ability { if (ability instanceof OptionalAdditionalSourceCosts) { sb.append(((OptionalAdditionalSourceCosts) ability).getCastMessageSuffix()); } - if (ability instanceof AlternativeSourceCosts && ((AlternativeSourceCosts) ability).isActivated()) { - sb.append(((AlternativeSourceCosts) ability).getCastMessageSuffix()); + if (ability instanceof AlternativeSourceCosts && ((AlternativeSourceCosts) ability).isActivated(this, game)) { + sb.append(((AlternativeSourceCosts) ability).getCastMessageSuffix(game)); } } return sb.toString(); diff --git a/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java b/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java new file mode 100644 index 00000000000..29f4a453b89 --- /dev/null +++ b/Mage/src/mage/abilities/common/DealsDamageGainLifeSourceTriggeredAbility.java @@ -0,0 +1,112 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.common; + +import static javax.xml.bind.JAXBIntrospector.getValue; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ + +public class DealsDamageGainLifeSourceTriggeredAbility extends TriggeredAbilityImpl { + + public DealsDamageGainLifeSourceTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainThatMuchLifeEffect(), false); + } + + public DealsDamageGainLifeSourceTriggeredAbility(final DealsDamageGainLifeSourceTriggeredAbility ability) { + super(ability); + } + + @Override + public DealsDamageGainLifeSourceTriggeredAbility copy() { + return new DealsDamageGainLifeSourceTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType().equals(GameEvent.EventType.DAMAGED_CREATURE) + || event.getType().equals(GameEvent.EventType.DAMAGED_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGED_PLANESWALKER)) { + if (event.getSourceId().equals(this.getSourceId())) { + for (Effect effect : this.getEffects()) { + effect.setValue("damage", event.getAmount()); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} deals damage, " + super.getRule(); + } +} + +class GainThatMuchLifeEffect extends OneShotEffect { + + public GainThatMuchLifeEffect() { + super(Outcome.GainLife); + this.staticText = "you gain that much life"; + } + + public GainThatMuchLifeEffect(final GainThatMuchLifeEffect effect) { + super(effect); + } + + @Override + public GainThatMuchLifeEffect copy() { + return new GainThatMuchLifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int amount = (Integer) getValue("damage"); + if (amount > 0) { + controller.gainLife(amount, game); + + } + return true; + } + return false; + } +} diff --git a/Mage/src/mage/abilities/condition/common/EvokedCondition.java b/Mage/src/mage/abilities/condition/common/EvokedCondition.java index df92cd75b40..9727bb9f350 100644 --- a/Mage/src/mage/abilities/condition/common/EvokedCondition.java +++ b/Mage/src/mage/abilities/condition/common/EvokedCondition.java @@ -59,7 +59,7 @@ public class EvokedCondition implements Condition { if (card != null) { for (Ability ability: card.getAbilities()) { if (ability instanceof EvokeAbility) { - if(((EvokeAbility) ability).isActivated()) { + if(((EvokeAbility) ability).isActivated(source, game)) { return true; } } diff --git a/Mage/src/mage/abilities/condition/common/ProwlCondition.java b/Mage/src/mage/abilities/condition/common/ProwlCondition.java index 86006f1a5d3..c539a4717a8 100644 --- a/Mage/src/mage/abilities/condition/common/ProwlCondition.java +++ b/Mage/src/mage/abilities/condition/common/ProwlCondition.java @@ -59,7 +59,7 @@ public class ProwlCondition implements Condition { if (card != null) { for (Ability ability: card.getAbilities()) { if (ability instanceof ProwlAbility) { - if(((ProwlAbility) ability).isActivated()) { + if(((ProwlAbility) ability).isActivated(source, game)) { return true; } } diff --git a/Mage/src/mage/abilities/costs/AlternativeCost2.java b/Mage/src/mage/abilities/costs/AlternativeCost2.java index 0689dadb18a..ec392996b1c 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCost2.java +++ b/Mage/src/mage/abilities/costs/AlternativeCost2.java @@ -27,6 +27,8 @@ */ package mage.abilities.costs; +import mage.game.Game; + /** * * @author LevelX2 @@ -78,9 +80,10 @@ public interface AlternativeCost2 extends Cost { /** * Returns if the cost was activated * + * @param game * @return */ - boolean isActivated(); + boolean isActivated(Game game); Cost getCost(); diff --git a/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java b/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java index 9b382e2b0af..6f4b788a388 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java +++ b/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java @@ -28,6 +28,8 @@ package mage.abilities.costs; +import mage.game.Game; + /** * Alternative costs * @@ -136,10 +138,11 @@ public class AlternativeCost2Impl > extends Co /** * Returns if the cost was activated * + * @param game * @return */ @Override - public boolean isActivated(){ + public boolean isActivated(Game game){ return activated; }; diff --git a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java index be374fa75ab..5b5ba7897ba 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -124,13 +124,13 @@ public class AlternativeCostSourceAbility extends StaticAbility { } rule = effect.getText(mode); if (rule != null) { - if (rule.startsWith("and ")) { + if (rule.startsWith("and ") || rule.startsWith("with ")) { endString = " "; } else if (rule.startsWith(",")) { endString = ""; diff --git a/Mage/src/mage/abilities/keyword/EvokeAbility.java b/Mage/src/mage/abilities/keyword/EvokeAbility.java index c9dcc8f010f..b1854e0d93f 100644 --- a/Mage/src/mage/abilities/keyword/EvokeAbility.java +++ b/Mage/src/mage/abilities/keyword/EvokeAbility.java @@ -31,8 +31,6 @@ package mage.abilities.keyword; import java.util.Iterator; import java.util.LinkedList; import java.util.List; - -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; @@ -48,6 +46,7 @@ import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.Card; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -61,7 +60,10 @@ public class EvokeAbility extends StaticAbility implements Alterna protected static final String EVOKE_KEYWORD = "Evoke"; protected static final String REMINDER_TEXT = "(You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield.)"; - protected List evokeCosts = new LinkedList(); + protected List evokeCosts = new LinkedList<>(); + + // needed to check activation status, if card changes zone after casting it + private int zoneChangeCounter = 0; public EvokeAbility(Card card, String manaString) { super(Zone.ALL, null); @@ -76,6 +78,7 @@ public class EvokeAbility extends StaticAbility implements Alterna public EvokeAbility(final EvokeAbility ability) { super(ability); this.evokeCosts.addAll(ability.evokeCosts); + this.zoneChangeCounter = ability.zoneChangeCounter; } @Override @@ -93,12 +96,17 @@ public class EvokeAbility extends StaticAbility implements Alterna for (AlternativeCost2 cost: evokeCosts) { cost.reset(); } + zoneChangeCounter = 0; } + @Override - public boolean isActivated() { - for (AlternativeCost2 cost: evokeCosts) { - if(cost.isActivated()) { - return true; + public boolean isActivated(Ability ability, Game game) { + Card card = game.getCard(sourceId); + if (card != null && card.getZoneChangeCounter() <= zoneChangeCounter +1) { + for (AlternativeCost2 cost: evokeCosts) { + if(cost.isActivated(game)) { + return true; + } } } return false; @@ -118,7 +126,7 @@ public class EvokeAbility extends StaticAbility implements Alterna for (AlternativeCost2 evokeCost: evokeCosts) { if (evokeCost.canPay(sourceId, controllerId, game) && player.chooseUse(Outcome.Benefit, new StringBuilder(EVOKE_KEYWORD).append(" the creature for ").append(evokeCost.getText(true)).append(" ?").toString(), game)) { - evokeCost.activate(); + activateEvoke(evokeCost, game); ability.getManaCostsToPay().clear(); ability.getCosts().clear(); for (Iterator it = ((Costs) evokeCost).iterator(); it.hasNext();) { @@ -133,7 +141,20 @@ public class EvokeAbility extends StaticAbility implements Alterna } } } - return isActivated(); + return isActivated(ability, game); + } + + private void activateEvoke(AlternativeCost2 cost, Game game) { + cost.activate(); + // remember zone change counter + if (zoneChangeCounter == 0) { + Card card = game.getCard(getSourceId()); + if (card != null) { + zoneChangeCounter = card.getZoneChangeCounter(); + } else { + throw new IllegalArgumentException("Evoke source card not found"); + } + } } @Override @@ -158,11 +179,11 @@ public class EvokeAbility extends StaticAbility implements Alterna } @Override - public String getCastMessageSuffix() { + public String getCastMessageSuffix(Game game) { StringBuilder sb = new StringBuilder(); int position = 0; for (AlternativeCost2 cost : evokeCosts) { - if (cost.isActivated()) { + if (cost.isActivated(game)) { sb.append(cost.getCastSuffixMessage(position)); ++position; } diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index 2feba3d91c4..b5ce4e92148 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -43,7 +43,6 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.VariableManaCost; import mage.cards.Card; -import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -185,7 +184,7 @@ public class KickerAbility extends StaticAbility implements Optio kickerCost.activate(); // remember zone change counter if (zoneChangeCounter == 0) { - Card card = game.getCard(sourceId); + Card card = game.getCard(getSourceId()); if (card != null) { zoneChangeCounter = card.getZoneChangeCounter(); } else { diff --git a/Mage/src/mage/abilities/keyword/ProwlAbility.java b/Mage/src/mage/abilities/keyword/ProwlAbility.java index 0e0ce7412ea..dbff6e7ed4c 100644 --- a/Mage/src/mage/abilities/keyword/ProwlAbility.java +++ b/Mage/src/mage/abilities/keyword/ProwlAbility.java @@ -97,10 +97,11 @@ public class ProwlAbility extends StaticAbility implements Alterna cost.reset(); } } + @Override - public boolean isActivated() { + public boolean isActivated(Ability ability, Game game) { for (AlternativeCost2 cost: prowlCosts) { - if(cost.isActivated()) { + if(cost.isActivated(game)) { return true; } } @@ -148,7 +149,7 @@ public class ProwlAbility extends StaticAbility implements Alterna } } } - return isActivated(); + return isActivated(ability, game); } @Override @@ -173,11 +174,11 @@ public class ProwlAbility extends StaticAbility implements Alterna } @Override - public String getCastMessageSuffix() { + public String getCastMessageSuffix(Game game) { StringBuilder sb = new StringBuilder(); int position = 0; for (AlternativeCost2 cost : prowlCosts) { - if (cost.isActivated()) { + if (cost.isActivated(game)) { sb.append(cost.getCastSuffixMessage(position)); ++position; } diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 3951e19e699..73a13cdd6f1 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -55,7 +55,7 @@ public enum CardRepository { private static final String JDBC_URL = "jdbc:sqlite:db/cards.db"; private static final String VERSION_ENTITY_NAME = "card"; - private static final long CARD_DB_VERSION = 31; + private static final long CARD_DB_VERSION = 32; private final Random random = new Random(); private Dao cardDao;