diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java index 23c02863284..68877f48eb8 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java @@ -16,6 +16,7 @@ public class MagicCardsImageSource implements CardImageSource { private static final Map setNameTokenReplacement = new HashMap() { { + put("C15", "commander-2015"); put("ORG", "oath-of-the-gatewatch"); put("EXP", "zendikar-expeditions"); put("BFZ", "battle-for-zendikar"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java index 3e1ed8c95b3..9a1c93babe8 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java @@ -99,6 +99,7 @@ public class WizardCardsImageSource implements CardImageSource { setsAliases.put("BTD", "Beatdown Box Set"); setsAliases.put("C13", "Commander 2013 Edition"); setsAliases.put("C14", "Commander 2014"); + setsAliases.put("C15", "Commander 2015"); setsAliases.put("CHK", "Champions of Kamigawa"); setsAliases.put("CHR", "Chronicles"); setsAliases.put("CMD", "Magic: The Gathering-Commander"); diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index fc6e7f06ec3..3617a698f38 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -64,6 +64,6 @@ ddd=gvl unh=uh dde=pvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK, OGW +ignore.urls=TOK, OGW, C15 # sets ordered by release time (newest goes first) -token.lookup.order=OGW,EXP,DDP,BFZ,FVD,FVE,FVL,FVR,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file +token.lookup.order=C15,OGW,EXP,DDP,BFZ,FVD,FVE,FVL,FVR,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Commander2015.java b/Mage.Sets/src/mage/sets/Commander2015.java new file mode 100644 index 00000000000..d1069bda220 --- /dev/null +++ b/Mage.Sets/src/mage/sets/Commander2015.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets; + +import java.util.GregorianCalendar; +import mage.cards.ExpansionSet; +import mage.constants.SetType; + +/** + * + * @author fireshoes + */ + +public class Commander2015 extends ExpansionSet { + + private static final Commander2015 fINSTANCE = new Commander2015(); + + public static Commander2015 getInstance() { + return fINSTANCE; + } + + private Commander2015() { + super("Commander 2015 Edition", "C15", "mage.sets.commander2015", new GregorianCalendar(2015, 11, 13).getTime(), SetType.SUPPLEMENTAL); + this.blockName = "Command Zone"; + } + +} diff --git a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java index 3dd3c0fc6d5..c364916e666 100644 --- a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java +++ b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java @@ -52,7 +52,6 @@ public class SkirkCommando extends CardImpl { super(ownerId, 47, "Skirk Commando", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.expansionSetCode = "ARC"; this.subtype.add("Goblin"); - this.subtype.add("Shaman"); this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/DustStalker.java b/Mage.Sets/src/mage/sets/battleforzendikar/DustStalker.java index cd751056f6b..15b94094f9c 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/DustStalker.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/DustStalker.java @@ -59,8 +59,7 @@ public class DustStalker extends CardImpl { public DustStalker(UUID ownerId) { super(ownerId, 202, "Dust Stalker", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); this.expansionSetCode = "BFZ"; - this.supertype.add("Creautre"); - this.supertype.add("Eldrazi"); + this.subtype.add("Eldrazi"); this.power = new MageInt(5); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/SerpentineSpike.java b/Mage.Sets/src/mage/sets/battleforzendikar/SerpentineSpike.java index b6de1e9f1a6..292dc45afd9 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/SerpentineSpike.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/SerpentineSpike.java @@ -39,6 +39,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -60,9 +61,23 @@ public class SerpentineSpike extends CardImpl { this.addAbility(ability); // Serpentine Spike deals 2 damage to target creature, 3 damage to another target creature, and 4 damage to a third target creature. If a creature dealt damage this way would die this turn, exile it instead. this.getSpellAbility().addEffect(new SerpentineSpikeEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (2 damage)"))); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (3 damage)"))); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (4 damage)"))); + + TargetCreaturePermanent target = new TargetCreaturePermanent(new FilterCreaturePermanent("creature (2 damage)")); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + + FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature (3 damage)"); + filter.add(new AnotherTargetPredicate(2)); + target = new TargetCreaturePermanent(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + + filter = new FilterCreaturePermanent("another target creature (4 damage)"); + filter.add(new AnotherTargetPredicate(3)); + target = new TargetCreaturePermanent(filter); + target.setTargetTag(3); + this.getSpellAbility().addTarget(target); + Effect effect = new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn); effect.setText("If a creature dealt damage this way would die this turn, exile it instead"); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheRealm.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheRealm.java index d973dc72a98..6afd9f2b81a 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheRealm.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GenjuOfTheRealm.java @@ -62,6 +62,7 @@ public class GenjuOfTheRealm extends CardImpl { super(ownerId, 151, "Genju of the Realm", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}"); this.expansionSetCode = "BOK"; this.subtype.add("Aura"); + this.supertype.add("Legendary"); // Enchant Land diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/MinamoSightbender.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/MinamoSightbender.java index 10062690021..e10d7262166 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/MinamoSightbender.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/MinamoSightbender.java @@ -62,7 +62,7 @@ public class MinamoSightbender extends CardImpl { this.subtype.add("Wizard"); this.power = new MageInt(1); - this.toughness = new MageInt(1); + this.toughness = new MageInt(2); // {X}, {T}: Target creature with power X or less can't be blocked this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedTargetEffect(), new ManaCostsImpl("{X}")); diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/WardOfPiety.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/WardOfPiety.java index 1edee8058ed..94b13e99c31 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/WardOfPiety.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/WardOfPiety.java @@ -28,11 +28,11 @@ package mage.sets.betrayersofkamigawa; import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RedirectionEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -44,7 +44,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreatureOrPlayer; import mage.target.common.TargetCreaturePermanent; @@ -60,7 +59,6 @@ public class WardOfPiety extends CardImpl { this.expansionSetCode = "BOK"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -84,10 +82,12 @@ public class WardOfPiety extends CardImpl { } } -class WardOfPietyPreventDamageTargetEffect extends PreventionEffectImpl { +class WardOfPietyPreventDamageTargetEffect extends RedirectionEffect { + + protected MageObjectReference redirectToObject; public WardOfPietyPreventDamageTargetEffect() { - super(Duration.EndOfTurn, 1, false, true); + super(Duration.EndOfTurn, 1, true); staticText = "The next 1 damage that would be dealt to enchanted creature this turn is dealt to target creature or player instead"; } @@ -106,35 +106,21 @@ class WardOfPietyPreventDamageTargetEffect extends PreventionEffectImpl { } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - // deal damage now - if (preventionData.getPreventedDamage() > 0) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " damage to " + permanent.getLogName() + " instead"); - // keep the original source id as it is redirecting - permanent.damage(preventionData.getPreventedDamage(), event.getSourceId(), game, false, true); - } - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null) { - game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " damage to " + player.getLogName() + " instead"); - // keep the original source id as it is redirecting - player.damage(preventionData.getPreventedDamage(), event.getSourceId(), game, false, true); - } - } - return false; + public void init(Ability source, Game game) { + super.init(source, game); + redirectToObject = new MageObjectReference(source.getTargets().get(0).getFirstTarget(), game); } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && event.getTargetId().equals(enchantment.getAttachedTo())) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null && event.getTargetId().equals(enchantment.getAttachedTo())) { + if (redirectToObject.equals(new MageObjectReference(source.getTargets().get(0).getFirstTarget(), game))) { + redirectTarget = source.getTargets().get(0); return true; } } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/CrushingPain.java b/Mage.Sets/src/mage/sets/championsofkamigawa/CrushingPain.java index 048fa176252..8d797e53819 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/CrushingPain.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/CrushingPain.java @@ -54,7 +54,7 @@ public class CrushingPain extends CardImpl { public CrushingPain (UUID ownerId) { super(ownerId, 162, "Crushing Pain", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "CHK"; - + this.subtype.add("Arcane"); // Crushing Pain deals 6 damage to target creature that was dealt damage this turn. this.getSpellAbility().addEffect(new DamageTargetEffect(6)); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/EtherealHaze.java b/Mage.Sets/src/mage/sets/championsofkamigawa/EtherealHaze.java index 8c6eb77fe52..308de9cef81 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/EtherealHaze.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/EtherealHaze.java @@ -45,7 +45,7 @@ public class EtherealHaze extends CardImpl { public EtherealHaze (UUID ownerId) { super(ownerId, 9, "Ethereal Haze", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "CHK"; - + this.subtype.add("Arcane"); // Prevent all damage that would be dealt by creatures this turn. this.getSpellAbility().addEffect(new PreventAllDamageByAllEffect(new FilterCreaturePermanent("creatures"), Duration.EndOfTurn, false)); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/UnearthlyBlizzard.java b/Mage.Sets/src/mage/sets/championsofkamigawa/UnearthlyBlizzard.java index 242df18ed16..29f76f4196d 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/UnearthlyBlizzard.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/UnearthlyBlizzard.java @@ -43,7 +43,7 @@ import mage.target.common.TargetCreaturePermanent; public class UnearthlyBlizzard extends CardImpl { public UnearthlyBlizzard(UUID ownerId) { - super(ownerId, 196, "Unearthly Blizzard", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); + super(ownerId, 196, "Unearthly Blizzard", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{R}"); this.expansionSetCode = "CHK"; this.subtype.add("Arcane"); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/VassalsDuty.java b/Mage.Sets/src/mage/sets/championsofkamigawa/VassalsDuty.java index 71230bc44e6..abd2d87510e 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/VassalsDuty.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/VassalsDuty.java @@ -31,8 +31,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RedirectionEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -42,8 +41,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.SupertypePredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.target.TargetPlayer; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -53,6 +51,7 @@ import mage.target.common.TargetControlledCreaturePermanent; public class VassalsDuty extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("legendary creature you control"); + static { filter.add(new SupertypePredicate("Legendary")); } @@ -61,10 +60,9 @@ public class VassalsDuty extends CardImpl { super(ownerId, 48, "Vassal's Duty", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); this.expansionSetCode = "CHK"; - // {1}: The next 1 damage that would be dealt to target legendary creature you control this turn is dealt to you instead. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VassalsDutyPreventDamageTargetEffect(Duration.EndOfTurn, 1), new GenericManaCost(1)); - ability.addTarget(new TargetControlledCreaturePermanent(1,1,filter, false)); + ability.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, false)); this.addAbility(ability); } @@ -78,10 +76,10 @@ public class VassalsDuty extends CardImpl { } } -class VassalsDutyPreventDamageTargetEffect extends PreventionEffectImpl { +class VassalsDutyPreventDamageTargetEffect extends RedirectionEffect { public VassalsDutyPreventDamageTargetEffect(Duration duration, int amount) { - super(duration, amount, false); + super(duration, amount, true); staticText = "The next " + amount + " damage that would be dealt to target legendary creature you control this turn is dealt to you instead"; } @@ -94,29 +92,13 @@ class VassalsDutyPreventDamageTargetEffect extends PreventionEffectImpl { return new VassalsDutyPreventDamageTargetEffect(this); } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionResult = preventDamageAction(event, source, game); - // deal damage now - if (preventionResult.getPreventedDamage() > 0) { - UUID redirectTo = source.getControllerId(); - Player player = game.getPlayer(redirectTo); - if (player != null) { - game.informPlayers("Dealing " + preventionResult.getPreventedDamage() + " to " + player.getLogName() + " instead"); - // keep the original source id as it is redirecting - player.damage(preventionResult.getPreventedDamage(), event.getSourceId(), game, false, true); - } - } - // damage amount is reduced or set to 0 so complete replacement of damage event is never neccessary - return false; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { - return game.getPermanent(event.getTargetId()) != null; - } + if (event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { + TargetPlayer target = new TargetPlayer(); + target.add(source.getControllerId(), game); + redirectTarget = target; + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java b/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java index fd05403b5b5..4505fa176ca 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java +++ b/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java @@ -29,6 +29,7 @@ package mage.sets.coldsnap; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.dynamicvalue.DynamicValue; @@ -59,7 +60,6 @@ public class LightningStorm extends CardImpl { super(ownerId, 89, "Lightning Storm", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{R}{R}"); this.expansionSetCode = "CSP"; - // Lightning Storm deals X damage to target creature or player, where X is 3 plus the number of charge counters on it. Effect effect = new DamageTargetEffect(new LightningStormCountCondition(CounterType.CHARGE)); effect.setText("{this} deals X damage to target creature or player, where X is 3 plus the number of charge counters on it"); @@ -67,7 +67,7 @@ public class LightningStorm extends CardImpl { this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); // Discard a land card: Put two charge counters on Lightning Storm. You may choose a new target for it. Any player may activate this ability but only if Lightning Storm is on the stack. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.STACK, - new LightningStormAddCounterEffect() , + new LightningStormAddCounterEffect(), new DiscardTargetCost(new TargetCardInHand(new FilterLandCard()))); ability.setMayActivate(TargetController.ANY); ability.addEffect(new InfoEffect("Any player may activate this ability but only if {this} is on the stack")); @@ -85,6 +85,7 @@ public class LightningStorm extends CardImpl { } class LightningStormCountCondition implements DynamicValue { + private final CounterType counter; public LightningStormCountCondition(CounterType counter) { @@ -141,7 +142,7 @@ class LightningStormAddCounterEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { spell.addCounters(CounterType.CHARGE.createInstance(2), game); - return spell.chooseNewTargets(game, source.getControllerId(), false, false, null); + return spell.chooseNewTargets(game, ((ActivatedAbilityImpl) source).getActivatorId(), false, false, null); } return false; } diff --git a/Mage.Sets/src/mage/sets/commander2015/EternalWitness.java b/Mage.Sets/src/mage/sets/commander2015/EternalWitness.java new file mode 100644 index 00000000000..6d8b8f5065b --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander2015/EternalWitness.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.commander2015; + +import java.util.UUID; + +/** + * + * @author fireshoes + */ +public class EternalWitness extends mage.sets.fifthdawn.EternalWitness { + + public EternalWitness(UUID ownerId) { + super(ownerId); + this.cardNumber = 183; + this.expansionSetCode = "C15"; + } + + public EternalWitness(final EternalWitness card) { + super(card); + } + + @Override + public EternalWitness copy() { + return new EternalWitness(this); + } +} diff --git a/Mage.Sets/src/mage/sets/commander2015/KalemneDiscipleOfIroas.java b/Mage.Sets/src/mage/sets/commander2015/KalemneDiscipleOfIroas.java new file mode 100644 index 00000000000..bd681bd53b1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/commander2015/KalemneDiscipleOfIroas.java @@ -0,0 +1,129 @@ +/* + * 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.commander2015; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.counter.AddCountersControllerEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.Filter; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class KalemneDiscipleOfIroas extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("a creature spell with converted mana cost 5 or greater"); + + static { + filterSpell.add(new CardTypePredicate(CardType.CREATURE)); + filterSpell.add(new ConvertedManaCostPredicate(Filter.ComparisonType.GreaterThan, 4)); + } + + public KalemneDiscipleOfIroas(UUID ownerId) { + super(ownerId, 999, "Kalemne, Disciple of Iroas", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + this.expansionSetCode = "C15"; + this.supertype.add("Legendary"); + this.subtype.add("Giant"); + this.subtype.add("Soldier"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever you cast a creature spell with converted mana cost 5 or greater, you get an experience counter. + Effect effect = new AddCountersControllerEffect(CounterType.EXPERIENCE.createInstance(1), false); + effect.setText("you get an experience counter"); + Ability ability = new SpellCastControllerTriggeredAbility(effect, filterSpell, false); + this.addAbility(ability); + + // Kalemne, Disciple of Iroas gets +1/+1 for each experience counter you have. + DynamicValue value = new SourceControllerExperienceCountersCount(); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(value, value, Duration.WhileOnBattlefield))); + } + + public KalemneDiscipleOfIroas(final KalemneDiscipleOfIroas card) { + super(card); + } + + @Override + public KalemneDiscipleOfIroas copy() { + return new KalemneDiscipleOfIroas(this); + } +} + +class SourceControllerExperienceCountersCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int amount = 0; + Player player = game.getPlayer(sourceAbility.getControllerId()); + if (player != null) { + amount = player.getCounters().getCount(CounterType.EXPERIENCE); + } + return amount; + } + + @Override + public DynamicValue copy() { + return new SourceControllerExperienceCountersCount(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "experience counter you have"; + } +} diff --git a/Mage.Sets/src/mage/sets/conflux/EtherswornAdjudicator.java b/Mage.Sets/src/mage/sets/conflux/EtherswornAdjudicator.java index 96d9df3d93d..6b5c29dcde7 100644 --- a/Mage.Sets/src/mage/sets/conflux/EtherswornAdjudicator.java +++ b/Mage.Sets/src/mage/sets/conflux/EtherswornAdjudicator.java @@ -63,6 +63,8 @@ public class EtherswornAdjudicator extends CardImpl { public EtherswornAdjudicator(UUID ownerId) { super(ownerId, 26, "Ethersworn Adjudicator", Rarity.MYTHIC, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}"); this.expansionSetCode = "CON"; + this.subtype.add("Vedalken"); + this.subtype.add("Knight"); this.power = new MageInt(4); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/sets/conflux/PathToExile.java b/Mage.Sets/src/mage/sets/conflux/PathToExile.java index 63f6a0c889a..ed66cc9597d 100644 --- a/Mage.Sets/src/mage/sets/conflux/PathToExile.java +++ b/Mage.Sets/src/mage/sets/conflux/PathToExile.java @@ -98,7 +98,7 @@ class PathToExileEffect extends OneShotEffect { if (player.searchLibrary(target, game)) { Card card = player.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); + player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); } } player.shuffleLibrary(game); diff --git a/Mage.Sets/src/mage/sets/darkascension/ArchdemonOfGreed.java b/Mage.Sets/src/mage/sets/darkascension/ArchdemonOfGreed.java index 42fc893a116..3fc7222beac 100644 --- a/Mage.Sets/src/mage/sets/darkascension/ArchdemonOfGreed.java +++ b/Mage.Sets/src/mage/sets/darkascension/ArchdemonOfGreed.java @@ -65,6 +65,7 @@ public class ArchdemonOfGreed extends CardImpl { super(ownerId, 71, "Archdemon of Greed", Rarity.RARE, new CardType[]{CardType.CREATURE}, ""); this.expansionSetCode = "DKA"; this.subtype.add("Demon"); + this.color.setBlack(true); this.nightCard = true; this.canTransform = true; diff --git a/Mage.Sets/src/mage/sets/dissension/UtopiaSprawl.java b/Mage.Sets/src/mage/sets/dissension/UtopiaSprawl.java index c92e93989b1..de6633ee6af 100644 --- a/Mage.Sets/src/mage/sets/dissension/UtopiaSprawl.java +++ b/Mage.Sets/src/mage/sets/dissension/UtopiaSprawl.java @@ -32,20 +32,18 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ChooseColorEffect; import mage.abilities.effects.common.ManaEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.mana.TriggeredManaAbility; import mage.cards.CardImpl; -import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -60,18 +58,13 @@ import mage.target.common.TargetLandPermanent; */ public class UtopiaSprawl extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("Forest"); + private static final FilterLandPermanent filter = new FilterLandPermanent("Forest", "Forest"); - static { - filter.add(new SubtypePredicate("Forest")); - } - public UtopiaSprawl(UUID ownerId) { super(ownerId, 99, "Utopia Sprawl", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.expansionSetCode = "DIS"; this.subtype.add("Aura"); - // Enchant Forest TargetPermanent auraTarget = new TargetLandPermanent(filter); this.getSpellAbility().addTarget(auraTarget); @@ -79,7 +72,7 @@ public class UtopiaSprawl extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); // As Utopia Sprawl enters the battlefield, choose a color. - this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect())); + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Detriment))); // Whenever enchanted Forest is tapped for mana, its controller adds one mana of the chosen color to his or her mana pool. this.addAbility(new UtopiaSprawlTriggeredAbility()); } @@ -94,42 +87,8 @@ public class UtopiaSprawl extends CardImpl { } } -class ChooseColorEffect extends OneShotEffect { - - public ChooseColorEffect() { - super(Outcome.BoostCreature); - staticText = "choose a color"; - } - - public ChooseColorEffect(final ChooseColorEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - ChoiceColor colorChoice = new ChoiceColor(); - if (player.choose(Outcome.Neutral, colorChoice, game)) { - game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + colorChoice.getChoice()); - game.getState().setValue(permanent.getId() + "_color", colorChoice.getColor()); - permanent.addInfo("chosen color", "Chosen color: " + colorChoice.getColor().getDescription() + "", game); - } - } - return false; - } - - @Override - public ChooseColorEffect copy() { - return new ChooseColorEffect(this); - } - -} - class UtopiaSprawlTriggeredAbility extends TriggeredManaAbility { - public UtopiaSprawlTriggeredAbility() { super(Zone.BATTLEFIELD, new UtopiaSprawlEffect()); } @@ -146,10 +105,7 @@ class UtopiaSprawlTriggeredAbility extends TriggeredManaAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent enchantment = game.getPermanent(this.getSourceId()); - if (enchantment != null && event.getSourceId().equals(enchantment.getAttachedTo())) { - return true; - } - return false; + return enchantment != null && event.getSourceId().equals(enchantment.getAttachedTo()); } @Override @@ -159,11 +115,10 @@ class UtopiaSprawlTriggeredAbility extends TriggeredManaAbility { @Override public String getRule() { - return "Whenever enchanted Forest is tapped for mana, its controller adds one mana of the chosen color to his or her mana pool"; + return "Whenever enchanted Forest is tapped for mana, its controller adds one mana of the chosen color to his or her mana pool."; } } - class UtopiaSprawlEffect extends ManaEffect { public UtopiaSprawlEffect() { @@ -178,9 +133,9 @@ class UtopiaSprawlEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { Permanent enchantment = game.getPermanent(source.getSourceId()); - if(enchantment != null){ + if (enchantment != null) { Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if(land != null){ + if (land != null) { Player player = game.getPlayer(land.getControllerId()); if (player != null) { player.getManaPool().addMana(getMana(game, source), game, source); @@ -205,4 +160,4 @@ class UtopiaSprawlEffect extends ManaEffect { public UtopiaSprawlEffect copy() { return new UtopiaSprawlEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/SmeltWardGatekeepers.java b/Mage.Sets/src/mage/sets/dragonsmaze/SmeltWardGatekeepers.java index 95f3582d94d..e79e5ae9797 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/SmeltWardGatekeepers.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/SmeltWardGatekeepers.java @@ -70,7 +70,7 @@ public class SmeltWardGatekeepers extends CardImpl { super(ownerId, 39, "Smelt-Ward Gatekeepers", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "DGM"; this.subtype.add("Human"); - this.subtype.add("Soldier"); + this.subtype.add("Warrior"); this.power = new MageInt(2); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java index 627a332ce4b..354d93ddef3 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/LivingLore.java @@ -71,7 +71,7 @@ public class LivingLore extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new LivingLoreExileEffect(), "exile an instant or sorcery card from your graveyard")); // Living Lore's power and toughness are each equal to the exiled card's converted mana cost. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LivingLoreSetPowerToughnessSourceEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new LivingLoreSetPowerToughnessSourceEffect())); // Whenever Living Lore deals combat damage, you may sacrifice it. If you do, you may cast the exiled card without paying its mana cost. this.addAbility(new DealsCombatDamageTriggeredAbility(new LivingLoreSacrificeEffect(), true)); @@ -106,14 +106,14 @@ class LivingLoreExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject != null && controller != null){ + Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); + if (sourcePermanent != null && controller != null) { TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard")); if (controller.chooseTarget(outcome, target, source, game)) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1); Card card = controller.getGraveyard().get(target.getFirstTarget(), game); if (card != null) { - controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.GRAVEYARD, true); + controller.moveCardsToExile(card, source, game, true, exileId, sourcePermanent.getIdName()); } } return true; @@ -126,7 +126,7 @@ class LivingLoreExileEffect extends OneShotEffect { class LivingLoreSetPowerToughnessSourceEffect extends ContinuousEffectImpl { public LivingLoreSetPowerToughnessSourceEffect() { - super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); + super(Duration.Custom, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); staticText = "{this}'s power and toughness are each equal to the exiled card's converted mana cost"; } @@ -141,20 +141,23 @@ class LivingLoreSetPowerToughnessSourceEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - MageObject mageObject = source.getSourceObject(game); Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && mageObject == null && new MageObjectReference(permanent, game).refersTo(mageObject, game)) { - discard(); - return false; + int zcc = game.getState().getZoneChangeCounter(source.getSourceId()); + if (permanent == null) { + permanent = game.getPermanentEntering(source.getSourceId()); + zcc++; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (permanent == null) { + return true; + } + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), zcc); if (exileId != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null) { return false; } Card exiledCard = null; - for (Card card :exileZone.getCards(game)) { + for (Card card : exileZone.getCards(game)) { exiledCard = card; break; } @@ -197,7 +200,7 @@ class LivingLoreSacrificeEffect extends OneShotEffect { ExileZone exileZone = game.getExile().getExileZone(exileId); Card exiledCard = null; if (exileZone != null) { - for (Card card :exileZone.getCards(game)) { + for (Card card : exileZone.getCards(game)) { exiledCard = card; break; } diff --git a/Mage.Sets/src/mage/sets/exodus/KorChant.java b/Mage.Sets/src/mage/sets/exodus/KorChant.java index 0269d443edd..3aa8d9effb6 100644 --- a/Mage.Sets/src/mage/sets/exodus/KorChant.java +++ b/Mage.Sets/src/mage/sets/exodus/KorChant.java @@ -35,6 +35,8 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -53,8 +55,15 @@ public class KorChant extends CardImpl { // All damage that would be dealt this turn to target creature you control by a source of your choice is dealt to another target creature instead. this.getSpellAbility().addEffect(new KorChantEffect()); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new KorChantSecondTarget()); + TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + + FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature"); + filter.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public KorChant(final KorChant card) { @@ -67,33 +76,8 @@ public class KorChant extends CardImpl { } } -class KorChantSecondTarget extends TargetCreaturePermanent { - - KorChantSecondTarget() { - super(); - this.targetName = "another creature"; - } - - KorChantSecondTarget(final KorChantSecondTarget target) { - super(target); - } - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - if (source.getTargets().get(0).getTargets().contains(id)) { - return false; - } - return super.canTarget(controllerId, id, source, game); - } - - @Override - public KorChantSecondTarget copy() { - return new KorChantSecondTarget(this); - } -} - class KorChantEffect extends RedirectionEffect { - + protected TargetSource target = new TargetSource(); KorChantEffect() { @@ -121,7 +105,7 @@ class KorChantEffect extends RedirectionEffect { public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGE_CREATURE; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(this.getTargetPointer().getFirst(game, source)) diff --git a/Mage.Sets/src/mage/sets/fatereforged/WildSlash.java b/Mage.Sets/src/mage/sets/fatereforged/WildSlash.java index 054006c589f..b2e5a665275 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/WildSlash.java +++ b/Mage.Sets/src/mage/sets/fatereforged/WildSlash.java @@ -29,7 +29,6 @@ package mage.sets.fatereforged; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.LockedInCondition; import mage.abilities.condition.common.FerociousCondition; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; @@ -41,7 +40,6 @@ 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.target.common.TargetCreatureOrPlayer; @@ -60,12 +58,12 @@ public class WildSlash extends CardImpl { ContinuousRuleModifyingEffect effect = new DamageCantBePreventedEffect(); effect.setText("Ferocious — If you control a creature with power 4 or greater, damage can't be prevented this turn.
"); this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect(effect, - new LockedInCondition(FerociousCondition.getInstance()))); - + new LockedInCondition(FerociousCondition.getInstance()))); + // Wild Slash deals 2 damage to target creature or player. this.getSpellAbility().addEffect(new DamageTargetEffect(2)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - + } public WildSlash(final WildSlash card) { diff --git a/Mage.Sets/src/mage/sets/fifthedition/ShanodinDryads.java b/Mage.Sets/src/mage/sets/fifthedition/ShanodinDryads.java index 9d7ff24671b..7d077635084 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/ShanodinDryads.java +++ b/Mage.Sets/src/mage/sets/fifthedition/ShanodinDryads.java @@ -43,6 +43,7 @@ public class ShanodinDryads extends CardImpl { public ShanodinDryads(UUID ownerId) { super(ownerId, 187, "Shanodin Dryads", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{G}"); this.expansionSetCode = "5ED"; + this.subtype.add("Nymph"); this.subtype.add("Dryad"); this.power = new MageInt(1); diff --git a/Mage.Sets/src/mage/sets/gatecrash/UrbanEvolution.java b/Mage.Sets/src/mage/sets/gatecrash/UrbanEvolution.java index 626a9642465..53128276399 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/UrbanEvolution.java +++ b/Mage.Sets/src/mage/sets/gatecrash/UrbanEvolution.java @@ -46,16 +46,12 @@ public class UrbanEvolution extends CardImpl { public UrbanEvolution(UUID ownerId) { super(ownerId, 204, "Urban Evolution", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{U}{G}"); this.expansionSetCode = "GTC"; - this.subtype.add("Wizard"); - //Draw three cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); //You may play an additional land this turn. this.getSpellAbility().addEffect(new PlayAdditionalLandsControllerEffect(1, Duration.EndOfTurn)); - - } public UrbanEvolution(final UrbanEvolution card) { diff --git a/Mage.Sets/src/mage/sets/guildpact/UlashtTheHateSeed.java b/Mage.Sets/src/mage/sets/guildpact/UlashtTheHateSeed.java index 6d99f312a7e..7903fdc13dc 100644 --- a/Mage.Sets/src/mage/sets/guildpact/UlashtTheHateSeed.java +++ b/Mage.Sets/src/mage/sets/guildpact/UlashtTheHateSeed.java @@ -73,7 +73,7 @@ public class UlashtTheHateSeed extends CardImpl { // Ulasht, the Hate Seed enters the battlefield with a +1/+1 counter on it for each other red creature you control and a +1/+1 counter on it for each other green creature you control. this.addAbility(new EntersBattlefieldAbility(new UlashtTheHateSeedEffect(), "with a +1/+1 counter on it for each other red creature you control and a +1/+1 counter on it for each other green creature you control.")); - + // {1}, Remove a +1/+1 counter from Ulasht: Choose one - Ulasht deals 1 damage to target creature; Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new GenericManaCost(1)); ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance())); @@ -101,7 +101,7 @@ class UlashtTheHateSeedEffect extends OneShotEffect { private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent(); private static final FilterControlledCreaturePermanent filterRed = new FilterControlledCreaturePermanent(); - + static { filterGreen.add(new AnotherPredicate()); filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); @@ -121,7 +121,7 @@ class UlashtTheHateSeedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (permanent != null && player != null) { int amount = game.getBattlefield().count(filterRed, source.getSourceId(), source.getControllerId(), game); amount += game.getBattlefield().count(filterGreen, source.getSourceId(), source.getControllerId(), game); @@ -138,4 +138,4 @@ class UlashtTheHateSeedEffect extends OneShotEffect { return new UlashtTheHateSeedEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/innistrad/UnbreathingHorde.java b/Mage.Sets/src/mage/sets/innistrad/UnbreathingHorde.java index c51d4a005b9..75a7eed6473 100644 --- a/Mage.Sets/src/mage/sets/innistrad/UnbreathingHorde.java +++ b/Mage.Sets/src/mage/sets/innistrad/UnbreathingHorde.java @@ -102,7 +102,7 @@ class UnbreathingHordeEffect1 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (permanent != null && player != null) { int amount = game.getBattlefield().countAll(filter1, source.getControllerId(), game) - 1; amount += player.getGraveyard().count(filter2, game); diff --git a/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java b/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java index b27d9824f79..dda545b51ea 100644 --- a/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java +++ b/Mage.Sets/src/mage/sets/jacevsvraska/DroolingGroodion.java @@ -44,6 +44,7 @@ import mage.constants.SubLayer; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -66,8 +67,17 @@ public class DroolingGroodion extends CardImpl { // {2}{B}{G}, Sacrifice a creature: Target creature gets +2/+2 until end of turn. Another target creature gets -2/-2 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DroolingGroodionEffect(), new ManaCostsImpl("{2}{B}{G}")); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent(), true))); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (first target)"))); - ability.addTarget(new TargetOtherCreaturePermanent(new FilterCreaturePermanent("creature (second target)"))); + + TargetCreaturePermanent target = new TargetCreaturePermanent(new FilterCreaturePermanent("creature (first target)")); + target.setTargetTag(1); + ability.addTarget(target); + + FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature (second target"); + filter.add(new AnotherTargetPredicate(2)); + target = new TargetCreaturePermanent(filter); + target.setTargetTag(2); + ability.addTarget(target); + this.addAbility(ability); } @@ -112,28 +122,3 @@ class DroolingGroodionEffect extends ContinuousEffectImpl { return true; } } - -class TargetOtherCreaturePermanent extends TargetCreaturePermanent { - - public TargetOtherCreaturePermanent(FilterCreaturePermanent filter) { - super(filter); - } - - public TargetOtherCreaturePermanent(final TargetOtherCreaturePermanent target) { - super(target); - } - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - if (source.getTargets().get(0).getTargets().contains(id)) { - return false; - } - return super.canTarget(controllerId, id, source, game); - } - - @Override - public TargetOtherCreaturePermanent copy() { - return new TargetOtherCreaturePermanent(this); - } - -} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/PensiveMinotaur.java b/Mage.Sets/src/mage/sets/journeyintonyx/PensiveMinotaur.java index 81c49ed6e25..6fcac8eda9a 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/PensiveMinotaur.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/PensiveMinotaur.java @@ -43,6 +43,7 @@ public class PensiveMinotaur extends CardImpl { super(ownerId, 105, "Pensive Minotaur", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.expansionSetCode = "JOU"; this.subtype.add("Minotaur"); + this.subtype.add("Warrior"); this.power = new MageInt(2); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java index 5fbf233ad1c..8ebbf2b0b79 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java +++ b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java @@ -46,7 +46,7 @@ import mage.target.common.TargetCreaturePermanent; public class FodderLaunch extends CardImpl { public FodderLaunch(UUID ownerId) { - super(ownerId, 114, "Fodder Launch", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}"); + super(ownerId, 114, "Fodder Launch", Rarity.UNCOMMON, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{3}{B}"); this.expansionSetCode = "LRW"; this.subtype.add("Goblin"); diff --git a/Mage.Sets/src/mage/sets/magic2010/HarmsWay.java b/Mage.Sets/src/mage/sets/magic2010/HarmsWay.java index 12a58405a48..11cf5c7c68a 100644 --- a/Mage.Sets/src/mage/sets/magic2010/HarmsWay.java +++ b/Mage.Sets/src/mage/sets/magic2010/HarmsWay.java @@ -30,8 +30,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RedirectionEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -69,91 +68,65 @@ public class HarmsWay extends CardImpl { } } -class HarmsWayPreventDamageTargetEffect extends PreventionEffectImpl { - - private final TargetSource target; - +class HarmsWayPreventDamageTargetEffect extends RedirectionEffect { + + private final TargetSource damageSource; + public HarmsWayPreventDamageTargetEffect() { - super(Duration.EndOfTurn, 2, false, true); + super(Duration.EndOfTurn, 2, true); staticText = "The next 2 damage that a source of your choice would deal to you and/or permanents you control this turn is dealt to target creature or player instead"; - this.target = new TargetSource(); + this.damageSource = new TargetSource(); } public HarmsWayPreventDamageTargetEffect(final HarmsWayPreventDamageTargetEffect effect) { super(effect); - this.target = effect.target.copy(); + this.damageSource = effect.damageSource.copy(); } @Override public HarmsWayPreventDamageTargetEffect copy() { return new HarmsWayPreventDamageTargetEffect(this); } - + @Override public void init(Ability source, Game game) { - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); super.init(source, game); } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - // deal damage now - if (preventionData.getPreventedDamage() > 0) { - UUID redirectTo = source.getFirstTarget(); - Permanent permanent = game.getPermanent(redirectTo); - if (permanent != null) { - game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + permanent.getLogName() + " instead"); - // keep the original source id as it is redirecting - permanent.damage(preventionData.getPreventedDamage(), event.getSourceId(), game, false, true); - discard(); - } - Player player = game.getPlayer(redirectTo); - if (player != null) { - game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + player.getLogName() + " instead"); - // keep the original source id as it is redirecting - player.damage(preventionData.getPreventedDamage(), event.getSourceId(), game, false, true); - discard(); + public boolean applies(GameEvent event, Ability source, Game game) { + // check source + MageObject object = game.getObject(event.getSourceId()); + if (object == null) { + game.informPlayers("Couldn't find source of damage"); + return false; + } + + if (!object.getId().equals(damageSource.getFirstTarget()) + && (!(object instanceof Spell) || !((Spell) object).getSourceId().equals(damageSource.getFirstTarget()))) { + return false; + } + this.redirectTarget = source.getTargets().get(0); + + // check target + // check permanent first + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + if (permanent.getControllerId().equals(source.getControllerId())) { + // it's your permanent + return true; } } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - // check source - MageObject object = game.getObject(event.getSourceId()); - if (object == null) { - game.informPlayers("Couldn't find source of damage"); - return false; - } - - if (!object.getId().equals(target.getFirstTarget()) - && (!(object instanceof Spell) || !((Spell) object).getSourceId().equals(target.getFirstTarget()))) { - return false; - } - - // check target - // check permanent first - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - if (permanent.getControllerId().equals(source.getControllerId())) { - // it's your permanent - return true; - } - } - // check player - Player player = game.getPlayer(event.getTargetId()); - if (player != null) { - if (player.getId().equals(source.getControllerId())) { - // it is you - return true; - } + // check player + Player player = game.getPlayer(event.getTargetId()); + if (player != null) { + if (player.getId().equals(source.getControllerId())) { + // it is you + return true; } } return false; } } - diff --git a/Mage.Sets/src/mage/sets/magic2010/Lifelink.java b/Mage.Sets/src/mage/sets/magic2010/Lifelink.java index b74e6c36ab6..b46a46d7013 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Lifelink.java +++ b/Mage.Sets/src/mage/sets/magic2010/Lifelink.java @@ -40,10 +40,6 @@ public class Lifelink extends mage.sets.magic2012.Lifelink { super(ownerId); this.cardNumber = 18; this.expansionSetCode = "M10"; - this.subtype.add("Aura"); - - this.color.setWhite(true); - } public Lifelink (final Lifelink card) { diff --git a/Mage.Sets/src/mage/sets/magic2011/AjanisPridemate.java b/Mage.Sets/src/mage/sets/magic2011/AjanisPridemate.java index 57e2e4dd192..4272c6c4093 100644 --- a/Mage.Sets/src/mage/sets/magic2011/AjanisPridemate.java +++ b/Mage.Sets/src/mage/sets/magic2011/AjanisPridemate.java @@ -48,7 +48,7 @@ import mage.game.events.GameEvent.EventType; public class AjanisPridemate extends CardImpl { public AjanisPridemate(UUID ownerId) { - super(ownerId, 3, "Ajani's Pridemate", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "1}{W}"); + super(ownerId, 3, "Ajani's Pridemate", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetCode = "M11"; this.subtype.add("Cat"); this.subtype.add("Soldier"); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java index 48dec655e1c..54631fd0c1e 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java @@ -1,16 +1,16 @@ /* * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -49,7 +49,9 @@ public class RedSunsZenith extends CardImpl { super(ownerId, 74, "Red Sun's Zenith", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{X}{R}"); this.expansionSetCode = "MBS"; - + // Red Sun's Zenith deals X damage to target creature or player. + // If a creature dealt damage this way would die this turn, exile it instead. + // Shuffle Red Sun's Zenith into its owner's library. this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new DamageTargetEffect(new ManacostVariableValue())); this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); diff --git a/Mage.Sets/src/mage/sets/modernmasters/ToothAndNail.java b/Mage.Sets/src/mage/sets/modernmasters/ToothAndNail.java index 30762aeb3d7..478914ee117 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/ToothAndNail.java +++ b/Mage.Sets/src/mage/sets/modernmasters/ToothAndNail.java @@ -102,7 +102,7 @@ class ToothAndNailPutCreatureOnBattlefieldEffect extends OneShotEffect { TargetCardInHand target = new TargetCardInHand(0, 2, new FilterCreatureCard("creature cards")); if (controller.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) { - return controller.moveCards(new CardsImpl(getTargetPointer().getTargets(game, source)).getCards(game), + return controller.moveCards(new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); } return false; diff --git a/Mage.Sets/src/mage/sets/odyssey/BalshanGriffin.java b/Mage.Sets/src/mage/sets/odyssey/BalshanGriffin.java index 02662a1d3bc..fe4767b9919 100644 --- a/Mage.Sets/src/mage/sets/odyssey/BalshanGriffin.java +++ b/Mage.Sets/src/mage/sets/odyssey/BalshanGriffin.java @@ -49,7 +49,7 @@ import mage.target.common.TargetCardInHand; public class BalshanGriffin extends CardImpl { public BalshanGriffin(UUID ownerId) { - super(ownerId, 67, "Balshan Griffin", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}"); + super(ownerId, 67, "Balshan Griffin", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.expansionSetCode = "ODY"; this.subtype.add("Griffin"); diff --git a/Mage.Sets/src/mage/sets/odyssey/FerventDenial.java b/Mage.Sets/src/mage/sets/odyssey/FerventDenial.java index 990232bc94e..24cb2a3e330 100644 --- a/Mage.Sets/src/mage/sets/odyssey/FerventDenial.java +++ b/Mage.Sets/src/mage/sets/odyssey/FerventDenial.java @@ -44,7 +44,7 @@ import mage.target.TargetSpell; public class FerventDenial extends CardImpl { public FerventDenial(UUID ownerId) { - super(ownerId, 86, "Fervent Denial", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{U}"); + super(ownerId, 86, "Fervent Denial", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); this.expansionSetCode = "ODY"; diff --git a/Mage.Sets/src/mage/sets/onslaught/FutureSight.java b/Mage.Sets/src/mage/sets/onslaught/FutureSight.java index 0c8dff1f1c6..929246b111d 100644 --- a/Mage.Sets/src/mage/sets/onslaught/FutureSight.java +++ b/Mage.Sets/src/mage/sets/onslaught/FutureSight.java @@ -35,7 +35,6 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterCard; /** * @@ -47,11 +46,10 @@ public class FutureSight extends CardImpl { super(ownerId, 84, "Future Sight", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}"); this.expansionSetCode = "ONS"; - // Play with the top card of your library revealed. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); // You may play the top card of your library. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(new FilterCard()))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect())); } public FutureSight(final FutureSight card) { diff --git a/Mage.Sets/src/mage/sets/onslaught/RiptideShapeshifter.java b/Mage.Sets/src/mage/sets/onslaught/RiptideShapeshifter.java index 72ff1eb3c97..615d8f2ee16 100644 --- a/Mage.Sets/src/mage/sets/onslaught/RiptideShapeshifter.java +++ b/Mage.Sets/src/mage/sets/onslaught/RiptideShapeshifter.java @@ -56,7 +56,7 @@ import mage.players.Player; public class RiptideShapeshifter extends CardImpl { public RiptideShapeshifter(UUID ownerId) { - super(ownerId, 109, "Riptide Shapeshifter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}"); + super(ownerId, 109, "Riptide Shapeshifter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.expansionSetCode = "ONS"; this.subtype.add("Shapeshifter"); diff --git a/Mage.Sets/src/mage/sets/planechase/RaziaBorosArchangel.java b/Mage.Sets/src/mage/sets/planechase/RaziaBorosArchangel.java index 720d19460c3..be5502dbe76 100644 --- a/Mage.Sets/src/mage/sets/planechase/RaziaBorosArchangel.java +++ b/Mage.Sets/src/mage/sets/planechase/RaziaBorosArchangel.java @@ -29,11 +29,12 @@ package mage.sets.planechase; import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.Effect; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RedirectionEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.VigilanceAbility; @@ -42,9 +43,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -72,11 +75,18 @@ public class RaziaBorosArchangel extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); - // {tap}: The next 3 damage that would be dealt to target creature you control this turn is dealt to another target creature instead. + // {T}: The next 3 damage that would be dealt to target creature you control this turn is dealt to another target creature instead. Effect effect = new RaziaBorosArchangelEffect(Duration.EndOfTurn, 3); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetCreaturePermanent()); + Target target = new TargetControlledCreaturePermanent(); + target.setTargetTag(1); + ability.addTarget(target); + + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature (damage is redirected to)"); + filter.add(new AnotherTargetPredicate(2)); + target = new TargetCreaturePermanent(filter); + target.setTargetTag(2); + ability.addTarget(target); this.addAbility(ability); } @@ -91,19 +101,17 @@ public class RaziaBorosArchangel extends CardImpl { } } -class RaziaBorosArchangelEffect extends PreventionEffectImpl { +class RaziaBorosArchangelEffect extends RedirectionEffect { - private int amount; + protected MageObjectReference redirectToObject; public RaziaBorosArchangelEffect(Duration duration, int amount) { - super(duration); - this.amount = amount; + super(duration, 3, true); staticText = "The next " + amount + " damage that would be dealt to target creature you control this turn is dealt to another target creature instead"; } public RaziaBorosArchangelEffect(final RaziaBorosArchangelEffect effect) { super(effect); - this.amount = effect.amount; } @Override @@ -117,42 +125,16 @@ class RaziaBorosArchangelEffect extends PreventionEffectImpl { } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), false); - if (!game.replaceEvent(preventEvent)) { - int prevented; - if (event.getAmount() >= this.amount) { - int damage = amount; - event.setAmount(event.getAmount() - amount); - this.used = true; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; - } else { - int damage = event.getAmount(); - event.setAmount(0); - amount -= damage; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; - } - - // deal damage now - if (prevented > 0) { - UUID redirectTo = source.getTargets().get(1).getFirstTarget(); - Permanent permanent = game.getPermanent(redirectTo); - if (permanent != null) { - game.informPlayers("Dealing " + prevented + " to " + permanent.getName() + " instead"); - // keep the original source id as it is redirecting - permanent.damage(prevented, event.getSourceId(), game, false, true); - } - } - } - return false; + public void init(Ability source, Game game) { + super.init(source, game); + redirectToObject = new MageObjectReference(source.getTargets().get(1).getFirstTarget(), game); } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (source.getTargets().getFirstTarget().equals(event.getTargetId())) { + if (event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { + if (redirectToObject.equals(new MageObjectReference(source.getTargets().get(1).getFirstTarget(), game))) { + redirectTarget = source.getTargets().get(1); return true; } } diff --git a/Mage.Sets/src/mage/sets/planechase/SilverMyr.java b/Mage.Sets/src/mage/sets/planechase/SilverMyr.java index 97f6440f77c..84d30675695 100644 --- a/Mage.Sets/src/mage/sets/planechase/SilverMyr.java +++ b/Mage.Sets/src/mage/sets/planechase/SilverMyr.java @@ -40,7 +40,6 @@ public class SilverMyr extends mage.sets.mirrodin.SilverMyr { super(ownerId); this.cardNumber = 126; this.expansionSetCode = "HOP"; - this.subtype.add("Myr"); } public SilverMyr (final SilverMyr card) { diff --git a/Mage.Sets/src/mage/sets/ravnica/GolgariGraveTroll.java b/Mage.Sets/src/mage/sets/ravnica/GolgariGraveTroll.java index 1673b718af2..d0c851b05b3 100644 --- a/Mage.Sets/src/mage/sets/ravnica/GolgariGraveTroll.java +++ b/Mage.Sets/src/mage/sets/ravnica/GolgariGraveTroll.java @@ -28,10 +28,6 @@ package mage.sets.ravnica; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -42,6 +38,10 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.keyword.DredgeAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.CardTypePredicate; @@ -104,7 +104,7 @@ class GolgariGraveTrollEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (permanent != null && player != null) { int amount = player.getGraveyard().count(filter, game); if (amount > 0) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SphinxOfTheChimes.java b/Mage.Sets/src/mage/sets/returntoravnica/SphinxOfTheChimes.java index aba60a810f2..21055514d8d 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SphinxOfTheChimes.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SphinxOfTheChimes.java @@ -61,7 +61,7 @@ public class SphinxOfTheChimes extends CardImpl { public SphinxOfTheChimes(UUID ownerId) { super(ownerId, 52, "Sphinx of the Chimes", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); this.expansionSetCode = "RTR"; - this.subtype.add("Bird"); + this.subtype.add("Sphinx"); this.power = new MageInt(5); this.toughness = new MageInt(6); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java index 34fed8bc030..0ba6ed34b44 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java @@ -55,6 +55,7 @@ public class MatsuTribeBirdstalker extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Snake"); this.subtype.add("Warrior"); + this.subtype.add("Archer"); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/OxiddaDaredevil.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/OxiddaDaredevil.java index 834ecd9a51d..65a8259d5bd 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/OxiddaDaredevil.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/OxiddaDaredevil.java @@ -58,9 +58,12 @@ public class OxiddaDaredevil extends CardImpl { public OxiddaDaredevil (UUID ownerId) { super(ownerId, 100, "Oxidda Daredevil", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.expansionSetCode = "SOM"; - this.color.setRed(true); + this.subtype.add("Goblin"); + this.subtype.add("Artificer"); + this.power = new MageInt(2); this.toughness = new MageInt(1); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); diff --git a/Mage.Sets/src/mage/sets/scourge/GoblinWarchief.java b/Mage.Sets/src/mage/sets/scourge/GoblinWarchief.java index 0d62d55ccd7..298f2be5c4a 100644 --- a/Mage.Sets/src/mage/sets/scourge/GoblinWarchief.java +++ b/Mage.Sets/src/mage/sets/scourge/GoblinWarchief.java @@ -60,6 +60,7 @@ public class GoblinWarchief extends CardImpl { super(ownerId, 97, "Goblin Warchief", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.expansionSetCode = "SCG"; this.subtype.add("Goblin"); + this.subtype.add("Warrior"); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/sets/stronghold/NomadsEnKor.java b/Mage.Sets/src/mage/sets/stronghold/NomadsEnKor.java index 2c2bf9a230f..1c3d0aa8da8 100644 --- a/Mage.Sets/src/mage/sets/stronghold/NomadsEnKor.java +++ b/Mage.Sets/src/mage/sets/stronghold/NomadsEnKor.java @@ -32,8 +32,10 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; import mage.target.common.TargetControlledCreaturePermanent; @@ -55,7 +57,7 @@ public class NomadsEnKor extends CardImpl { this.toughness = new MageInt(1); // {0}: The next 1 damage that would be dealt to Nomads en-Kor this turn is dealt to target creature you control instead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShamanEnKorPreventionEffect(), new GenericManaCost(0)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, 1, true), new GenericManaCost(0)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -68,4 +70,4 @@ public class NomadsEnKor extends CardImpl { public NomadsEnKor copy() { return new NomadsEnKor(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/stronghold/ShamanEnKor.java b/Mage.Sets/src/mage/sets/stronghold/ShamanEnKor.java index 97c5d5349e0..e112c7f7821 100644 --- a/Mage.Sets/src/mage/sets/stronghold/ShamanEnKor.java +++ b/Mage.Sets/src/mage/sets/stronghold/ShamanEnKor.java @@ -29,14 +29,13 @@ package mage.sets.stronghold; import java.util.UUID; import mage.MageInt; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.RedirectionEffect; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -44,11 +43,10 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamageEvent; 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.TargetSource; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -70,12 +68,13 @@ public class ShamanEnKor extends CardImpl { this.toughness = new MageInt(2); // {0}: The next 1 damage that would be dealt to Shaman en-Kor this turn is dealt to target creature you control instead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShamanEnKorPreventionEffect(), new GenericManaCost(0)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, 1, true), new GenericManaCost(0)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); - + // {1}{W}: The next time a source of your choice would deal damage to target creature this turn, that damage is dealt to Shaman en-Kor instead. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShamanEnKorReplacementEffect(), new ManaCostsImpl("{1}{W}")); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShamanEnKorRedirectFromTargetEffect(), new ManaCostsImpl("{1}{W}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -90,129 +89,43 @@ public class ShamanEnKor extends CardImpl { } } -class ShamanEnKorPreventionEffect extends PreventionEffectImpl { - - ShamanEnKorPreventionEffect() { - super(Duration.EndOfTurn, 1, false); - staticText = "The next 1 damage that would be dealt to {this} this turn is dealt to target creature you control instead."; - } - - ShamanEnKorPreventionEffect(final ShamanEnKorPreventionEffect effect) { - super(effect); - } - - @Override - public ShamanEnKorPreventionEffect copy() { - return new ShamanEnKorPreventionEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionResult = preventDamageAction(event, source, game); - if (preventionResult.getPreventedDamage() > 0) { - Permanent redirectTo = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (redirectTo != null) { - game.informPlayers("Dealing " + preventionResult.getPreventedDamage() + " to " + redirectTo.getName() + " instead."); - DamageEvent damageEvent = (DamageEvent) event; - redirectTo.damage(preventionResult.getPreventedDamage(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - } - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getSourceId())) { - return game.getPermanent(getTargetPointer().getFirst(game, source)) != null; - } - } - return false; - } -} +class ShamanEnKorRedirectFromTargetEffect extends RedirectionEffect { -class ShamanEnKorReplacementEffect extends ReplacementEffectImpl { - - protected TargetSource targetSource; + protected MageObjectReference sourceObject; - ShamanEnKorReplacementEffect() { - super(Duration.EndOfTurn, Outcome.RedirectDamage); + ShamanEnKorRedirectFromTargetEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, true); staticText = "The next time a source of your choice would deal damage to target creature this turn, that damage is dealt to {this} instead"; } - ShamanEnKorReplacementEffect(final ShamanEnKorReplacementEffect effect) { + ShamanEnKorRedirectFromTargetEffect(final ShamanEnKorRedirectFromTargetEffect effect) { super(effect); - targetSource = effect.targetSource; + sourceObject = effect.sourceObject; } @Override public void init(Ability source, Game game) { Player player = game.getPlayer(source.getControllerId()); - TargetSource target = new TargetSource(); - target.setNotTarget(true); if (player != null) { + TargetSource target = new TargetSource(); target.choose(Outcome.PreventDamage, player.getId(), source.getSourceId(), game); - this.targetSource = target; + this.sourceObject = new MageObjectReference(target.getFirstTarget(), game); + } else { + discard(); } } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.DAMAGE_CREATURE; } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used) { - if (targetSource != null) { - if (event.getSourceId().equals(targetSource.getFirstTarget())) { - // check source - MageObject object = game.getObject(event.getSourceId()); - if (object == null) { - game.informPlayers("Couldn't find source of damage"); - return false; - } - else { - if (event.getTargetId().equals(source.getFirstTarget())) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - return true; - } - } - } - } - } - } - return false; - } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent)event; - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - // get name of old target - Permanent targetPermanent = game.getPermanent(event.getTargetId()); - StringBuilder message = new StringBuilder(); - message.append(sourcePermanent.getName()).append(": gets "); - message.append(damageEvent.getAmount()).append(" damage redirected from "); - if (targetPermanent != null) { - message.append(targetPermanent.getName()); - } - else { - Player targetPlayer = game.getPlayer(event.getTargetId()); - if (targetPlayer != null) { - message.append(targetPlayer.getLogName()); - } - else { - message.append("unknown"); - } - } - game.informPlayers(message.toString()); - // redirect damage - this.used = true; - sourcePermanent.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - return true; + public boolean applies(GameEvent event, Ability source, Game game) { + if (sourceObject.equals(new MageObjectReference(event.getSourceId(), game))) { + redirectTarget = new TargetPermanent(); + redirectTarget.add(source.getSourceId(), game); + return event.getTargetId().equals(getTargetPointer().getFirst(game, source)); } return false; } @@ -223,7 +136,7 @@ class ShamanEnKorReplacementEffect extends ReplacementEffectImpl { } @Override - public ShamanEnKorReplacementEffect copy() { - return new ShamanEnKorReplacementEffect(this); + public ShamanEnKorRedirectFromTargetEffect copy() { + return new ShamanEnKorRedirectFromTargetEffect(this); } } diff --git a/Mage.Sets/src/mage/sets/tempest/CursedScroll.java b/Mage.Sets/src/mage/sets/tempest/CursedScroll.java index 110111ffe08..2f2c326a895 100644 --- a/Mage.Sets/src/mage/sets/tempest/CursedScroll.java +++ b/Mage.Sets/src/mage/sets/tempest/CursedScroll.java @@ -29,9 +29,6 @@ package mage.sets.tempest; import java.util.UUID; import mage.MageObject; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -42,7 +39,9 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.Cards; import mage.cards.CardsImpl; +import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -91,15 +90,15 @@ class CursedScrollEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); - if (sourceObject != null && you != null && cardName != null && !cardName.isEmpty()) { - if (you.getHand().size() > 0) { + if (sourceObject != null && controller != null && cardName != null && !cardName.isEmpty()) { + if (controller.getHand().size() > 0) { Cards revealed = new CardsImpl(); - Card card = you.getHand().getRandom(game); + Card card = controller.getHand().getRandom(game); revealed.add(card); - you.revealCards(sourceObject.getName(), revealed, game); + controller.revealCards(sourceObject.getIdName(), revealed, game); if (card.getName().equals(cardName)) { Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (creature != null) { diff --git a/Mage.Sets/src/mage/sets/tempestremastered/Deadshot.java b/Mage.Sets/src/mage/sets/tempestremastered/Deadshot.java index e2fd17d8826..1aa0222736a 100644 --- a/Mage.Sets/src/mage/sets/tempestremastered/Deadshot.java +++ b/Mage.Sets/src/mage/sets/tempestremastered/Deadshot.java @@ -36,29 +36,39 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.SecondTargetPointer; /** * * @author fireshoes */ public class Deadshot extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature"); + static { + filter.add(new AnotherTargetPredicate(2)); + } + public Deadshot(UUID ownerId) { super(ownerId, 129, "Deadshot", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{R}"); this.expansionSetCode = "TPR"; // Tap target creature. this.getSpellAbility().addEffect(new TapTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - + TargetCreaturePermanent target = new TargetCreaturePermanent(); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + // It deals damage equal to its power to another target creature. this.getSpellAbility().addEffect(new DeadshotDamageEffect()); - this.getSpellAbility().addTarget(new DeadshotTargetCreaturePermanent(filter)); + target = new TargetCreaturePermanent(filter); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); } public Deadshot(final Deadshot card) { @@ -80,6 +90,7 @@ class DeadshotDamageEffect extends OneShotEffect { public DeadshotDamageEffect(final DeadshotDamageEffect effect) { super(effect); + this.setTargetPointer(new SecondTargetPointer()); } @Override @@ -89,10 +100,10 @@ class DeadshotDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent ownCreature = game.getPermanent(source.getFirstTarget()); + Permanent ownCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); if (ownCreature != null) { int damage = ownCreature.getPower().getValue(); - Permanent targetCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetCreature != null) { targetCreature.damage(damage, ownCreature.getId(), game, false, true); return true; @@ -101,27 +112,3 @@ class DeadshotDamageEffect extends OneShotEffect { return false; } } - -class DeadshotTargetCreaturePermanent extends TargetCreaturePermanent { - - public DeadshotTargetCreaturePermanent(FilterCreaturePermanent filter) { - super(filter); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - if (source.getTargets().getFirstTarget().equals(id)) { - return false; - } - return super.canTarget(id, source, game); - } - - @Override - public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { - if (source.getTargets().getFirstTarget().equals(id)) { - return false; - } - return super.canTarget(controllerId, id, source, game); - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/tempestremastered/SpiritEnKor.java b/Mage.Sets/src/mage/sets/tempestremastered/SpiritEnKor.java index e07c177ea74..4b4998f4949 100644 --- a/Mage.Sets/src/mage/sets/tempestremastered/SpiritEnKor.java +++ b/Mage.Sets/src/mage/sets/tempestremastered/SpiritEnKor.java @@ -32,18 +32,13 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -62,9 +57,10 @@ public class SpiritEnKor extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // {0}: The next 1 damage that would be dealt to Spirit en-Kor this turn is dealt to target creature you control instead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpiritEnKorPreventionEffect(), new GenericManaCost(0)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, 1, true), new GenericManaCost(0)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -78,44 +74,3 @@ public class SpiritEnKor extends CardImpl { return new SpiritEnKor(this); } } - -class SpiritEnKorPreventionEffect extends PreventionEffectImpl { - - SpiritEnKorPreventionEffect() { - super(Duration.EndOfTurn, 1, false); - staticText = "The next 1 damage that would be dealt to {this} this turn is dealt to target creature you control instead."; - } - - SpiritEnKorPreventionEffect(final SpiritEnKorPreventionEffect effect) { - super(effect); - } - - @Override - public SpiritEnKorPreventionEffect copy() { - return new SpiritEnKorPreventionEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionResult = preventDamageAction(event, source, game); - if (preventionResult.getPreventedDamage() > 0) { - Permanent redirectTo = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (redirectTo != null) { - game.informPlayers("Dealing " + preventionResult.getPreventedDamage() + " to " + redirectTo.getName() + " instead."); - DamageEvent damageEvent = (DamageEvent) event; - redirectTo.damage(preventionResult.getPreventedDamage(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - } - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getSourceId())) { - return game.getPermanent(getTargetPointer().getFirst(game, source)) != null; - } - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/tempestremastered/WarriorEnKor.java b/Mage.Sets/src/mage/sets/tempestremastered/WarriorEnKor.java index 4d991b1b1f0..4587dbd24e6 100644 --- a/Mage.Sets/src/mage/sets/tempestremastered/WarriorEnKor.java +++ b/Mage.Sets/src/mage/sets/tempestremastered/WarriorEnKor.java @@ -32,17 +32,12 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -61,7 +56,8 @@ public class WarriorEnKor extends CardImpl { this.toughness = new MageInt(2); // {0}: The next 1 damage that would be dealt to Warrior en-Kor this turn is dealt to target creature you control instead. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new WarriorEnKorPreventionEffect(), new GenericManaCost(0)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, 1, true), new GenericManaCost(0)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -75,44 +71,3 @@ public class WarriorEnKor extends CardImpl { return new WarriorEnKor(this); } } - -class WarriorEnKorPreventionEffect extends PreventionEffectImpl { - - WarriorEnKorPreventionEffect() { - super(Duration.EndOfTurn, 1, false); - staticText = "The next 1 damage that would be dealt to {this} this turn is dealt to target creature you control instead."; - } - - WarriorEnKorPreventionEffect(final WarriorEnKorPreventionEffect effect) { - super(effect); - } - - @Override - public WarriorEnKorPreventionEffect copy() { - return new WarriorEnKorPreventionEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionResult = preventDamageAction(event, source, game); - if (preventionResult.getPreventedDamage() > 0) { - Permanent redirectTo = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (redirectTo != null) { - game.informPlayers("Dealing " + preventionResult.getPreventedDamage() + " to " + redirectTo.getName() + " instead."); - DamageEvent damageEvent = (DamageEvent) event; - redirectTo.damage(preventionResult.getPreventedDamage(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - } - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getSourceId())) { - return game.getPermanent(getTargetPointer().getFirst(game, source)) != null; - } - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/tenthedition/Clone.java b/Mage.Sets/src/mage/sets/tenthedition/Clone.java index 6869479699a..6fa99a23821 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Clone.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Clone.java @@ -50,11 +50,6 @@ public class Clone extends CardImpl { this.toughness = new MageInt(0); // You may have Clone enter the battlefield as a copy of any creature on the battlefield. -// ; -// Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect( -// new CopyPermanentEffect(), -// "You may have {this} enter the battlefield as a copy of any creature on the battlefield", -// true)); this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(), true)); } diff --git a/Mage.Sets/src/mage/sets/tenthedition/HighwayRobber.java b/Mage.Sets/src/mage/sets/tenthedition/HighwayRobber.java index 6937d8e1ef0..6b55da06104 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/HighwayRobber.java +++ b/Mage.Sets/src/mage/sets/tenthedition/HighwayRobber.java @@ -48,7 +48,6 @@ public class HighwayRobber extends CardImpl { super(ownerId, 150, "Highway Robber", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.expansionSetCode = "10E"; this.subtype.add("Human"); - this.subtype.add("Rogue"); this.subtype.add("Mercenary"); this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/PatternOfRebirth.java b/Mage.Sets/src/mage/sets/urzasdestiny/PatternOfRebirth.java index 017063b773e..038d5fda7dc 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/PatternOfRebirth.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/PatternOfRebirth.java @@ -32,12 +32,13 @@ import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayTargetPlayerEffect; 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.filter.common.FilterCreatureCard; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; @@ -54,7 +55,6 @@ public class PatternOfRebirth extends CardImpl { this.expansionSetCode = "UDS"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -63,9 +63,9 @@ public class PatternOfRebirth extends CardImpl { this.addAbility(ability); // When enchanted creature dies, that creature's controller may search his or her library for a creature card and put that card onto the battlefield. If that player does, he or she shuffles his or her library. - Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterCreatureCard()), false, true, Outcome.PutCreatureInPlay); + Effect effect = new SearchLibraryPutInPlayTargetPlayerEffect(new TargetCardInLibrary(new FilterCreatureCard()), false, true, Outcome.PutCreatureInPlay); effect.setText("that creature's controller may search his or her library for a creature card and put that card onto the battlefield. If that player does, he or she shuffles his or her library"); - this.addAbility(new DiesAttachedTriggeredAbility(effect, "enchanted creature", true, true)); + this.addAbility(new DiesAttachedTriggeredAbility(effect, "enchanted creature", true, true, SetTargetPointer.ATTACHED_TO_CONTROLLER)); } diff --git a/Mage.Sets/src/mage/sets/urzassaga/GorillaWarrior.java b/Mage.Sets/src/mage/sets/urzassaga/GorillaWarrior.java index 5c20c56b30c..3149b5f07a4 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/GorillaWarrior.java +++ b/Mage.Sets/src/mage/sets/urzassaga/GorillaWarrior.java @@ -44,6 +44,7 @@ public class GorillaWarrior extends CardImpl { super(ownerId, 256, "Gorilla Warrior", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.expansionSetCode = "USG"; this.subtype.add("Ape"); + this.subtype.add("Warrior"); this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/sets/urzassaga/Sunder.java b/Mage.Sets/src/mage/sets/urzassaga/Sunder.java index ee8ff761003..fee91fcee5e 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Sunder.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Sunder.java @@ -46,7 +46,7 @@ import mage.game.permanent.Permanent; public class Sunder extends CardImpl { public Sunder(UUID ownerId) { - super(ownerId, 101, "Sunder", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{U}"); + super(ownerId, 101, "Sunder", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); this.expansionSetCode = "USG"; // Return all lands to their owners' hands. diff --git a/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java b/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java index cf786931cf8..a83b34aa210 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArrowVolleyTrap.java @@ -47,6 +47,7 @@ public class ArrowVolleyTrap extends CardImpl { public ArrowVolleyTrap(UUID ownerId) { super(ownerId, 2, "Arrow Volley Trap", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{W}{W}"); this.expansionSetCode = "ZEN"; + this.subtype.add("Trap"); // If four or more creatures are attacking, you may pay {1}{W} rather than pay Arrow Volley Trap's mana cost. this.getSpellAbility().addAlternativeCost(new ArrowVolleyTrapAlternativeCost()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SearchEntersBattlefieldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SearchEntersBattlefieldTest.java new file mode 100644 index 00000000000..b097f3a7eef --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/SearchEntersBattlefieldTest.java @@ -0,0 +1,59 @@ +/* + * 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.enters; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SearchEntersBattlefieldTest extends CardTestPlayerBase { + + @Test + public void testLandAfterFetchUntapped() { + addCard(Zone.HAND, playerA, "Verdant Catacombs"); + addCard(Zone.LIBRARY, playerA, "Forest"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Verdant Catacombs"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Pay"); + setChoice(playerA, "Forest"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Verdant Catacombs", 1); + assertPermanentCount(playerA, "Forest", 1); + assertTapped("Forest", false); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java index 0cd925879a7..f67350c1d85 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java @@ -115,7 +115,7 @@ public class SpliceOnArcaneTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. // You gain X life. - addCard(Zone.HAND, playerA, "Nourishing Shoal", 1); + addCard(Zone.HAND, playerA, "Nourishing Shoal", 1); // {X}{G}{G} addCard(Zone.HAND, playerA, "Giant Growth", 1); // You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step. // Splice onto Arcane {2}{R}{R} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java index 9ed98cca08f..8e66883abc3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java @@ -147,4 +147,20 @@ public class SuspendTest extends CardTestPlayerBase { assertCounterOnExiledCardCount("Deep-Sea Kraken", CounterType.TIME, 8); // -1 from spell of player B } + + @Test + public void testAncestralVisionCantBeCastDirectly() { + // Suspend 4-{U} + // Target player draws three cards. + addCard(Zone.HAND, playerA, "Ancestral Vision", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Vision", playerA); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); + assertHandCount(playerA, "Ancestral Vision", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.java new file mode 100644 index 00000000000..3b95c44288e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/RedirectDamageToPlaneswalkerTest.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 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 RedirectDamageToPlaneswalkerTest extends CardTestPlayerBase { + + @Test + public void testDirectDamage() { + // +2: Look at the top card of target player's library. You may put that card on the bottom of that player's library. + // 0: Draw three cards, then put two cards from your hand on top of your library in any order. + // −1: Return target creature to its owner's hand. + addCard(Zone.BATTLEFIELD, playerA, "Jace, the Mind Sculptor"); // starts with 3 Loyality counters + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", playerB); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + setChoice(playerB, "Yes"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Jace, the Mind Sculptor", 1); + assertCounterCount("Jace, the Mind Sculptor", CounterType.LOYALTY, 2); // 3 + 2 - 3 = 2 + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/HarmsWayRedirectDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/HarmsWayRedirectDamageTest.java similarity index 57% rename from Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/HarmsWayRedirectDamageTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/HarmsWayRedirectDamageTest.java index f609e5ed8e8..7f7508a5ab6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/HarmsWayRedirectDamageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/HarmsWayRedirectDamageTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.replacement.prevent; +package org.mage.test.cards.replacement.redirect; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -6,15 +6,17 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * Harm's Way: - * The next 2 damage that a source of your choice would deal to you and/or permanents you control this turn is dealt to target creature or player instead. + * Harm's Way: The next 2 damage that a source of your choice would deal to you + * and/or permanents you control this turn is dealt to target creature or player + * instead. * * @author noxx */ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase { /** - * Tests that 2 of 3 damage is redirected while 1 damage is still dealt to original target + * Tests that 2 of 3 damage is redirected while 1 damage is still dealt to + * original target */ @Test public void testRedirectTwoDamage() { @@ -51,7 +53,7 @@ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase { attack(2, playerB, "Craw Wurm"); castSpell(2, PhaseStep.DECLARE_BLOCKERS, playerA, "Harm's Way", playerB); setChoice(playerA, "Craw Wurm"); - + setStopAt(2, PhaseStep.END_TURN); execute(); @@ -79,8 +81,11 @@ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Magma Phoenix"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harm's Way", playerB); - setChoice(playerA, "Magma Phoenix"); - /** When Magma Phoenix dies, Magma Phoenix deals 3 damage to each creature and each player **/ + setChoice(playerA, "Magma Phoenix"); + /** + * When Magma Phoenix dies, Magma Phoenix deals 3 damage to each + * creature and each player * + */ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Magma Phoenix"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); @@ -91,4 +96,38 @@ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase { assertLife(playerB, 15); // 3 damage from dying Phoenix directly and 2 redirected damage from playerA } + /** + * Tests that not preventable damage is redirected + */ + @Test + public void testRedirectNotPreventableDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + // Ferocious If you control a creature with power 4 or greater, damage can't be prevented this turn. + // Wild Slash deals 2 damage to target creature or player. + addCard(Zone.HAND, playerA, "Wild Slash"); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Serra Angel"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // The next 2 damage that a source of your choice would deal to you and/or permanents + // you control this turn is dealt to target creature or player instead. + addCard(Zone.HAND, playerB, "Harm's Way"); // {W} + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + addCard(Zone.BATTLEFIELD, playerB, "Birds of Paradise"); + + // the 2 damage can't be prevented and have to be redirected to Silvercoat Lion of player A + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Slash", "Birds of Paradise"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Harm's Way", "Silvercoat Lion", "Wild Slash"); + setChoice(playerB, "Wild Slash"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wild Slash", 1); + assertGraveyardCount(playerB, "Harm's Way", 1); + assertPermanentCount(playerB, "Birds of Paradise", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/ShamenEnKorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/ShamenEnKorTest.java new file mode 100644 index 00000000000..a8ffe681e75 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/ShamenEnKorTest.java @@ -0,0 +1,94 @@ +/* + * 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.replacement.redirect; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class ShamenEnKorTest extends CardTestPlayerBase { + + /** + * Tests that 2 of 3 damage is redirected while 1 damage is still dealt to + * original target + */ + @Test + public void testFirstAbilityNonCombatDamage() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + // {0}: The next 1 damage that would be dealt to Shaman en-Kor this turn is dealt to target creature you control instead. + // {1}{W}: The next time a source of your choice would deal damage to target creature this turn, that damage is dealt to Shaman en-Kor instead. + addCard(Zone.BATTLEFIELD, playerB, "Shaman en-Kor"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Shaman en-Kor"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{0}: The next 1 damage", "Silvercoat Lion", "Lightning Bolt"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{0}: The next 1 damage", "Silvercoat Lion", "Lightning Bolt"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPermanentCount(playerB, "Shaman en-Kor", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + } + + @Test + public void testSecondAbilityNonCombatDamage() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + // {0}: The next 1 damage that would be dealt to Shaman en-Kor this turn is dealt to target creature you control instead. + // {1}{W}: The next time a source of your choice would deal damage to target creature this turn, that damage is dealt to Shaman en-Kor instead. + addCard(Zone.BATTLEFIELD, playerB, "Shaman en-Kor"); // 1/2 + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{W}: The next time", "Silvercoat Lion", "Lightning Bolt"); + setChoice(playerB, "Lightning Bolt"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Shaman en-Kor", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/WardOfPietyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/WardOfPietyTest.java new file mode 100644 index 00000000000..17b12464d7f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/WardOfPietyTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.replacement.redirect; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class WardOfPietyTest extends CardTestPlayerBase { + + @Test + public void testNonCombatDamageToPlayer() { + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + // Enchant creature + // {1}{W}: The next 1 damage that would be dealt to enchanted creature this turn is dealt to target creature or player instead. + addCard(Zone.HAND, playerA, "Ward of Piety"); // {1}{W} + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ward of Piety", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{1}{W}: The next 1 damage", playerB); + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{1}{W}: The next 1 damage", playerB); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertPermanentCount(playerA, "Ward of Piety", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 18); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java index ad4f7febe71..fe2d8ea9fb3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java @@ -192,4 +192,104 @@ public class OmniscienceTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Pillarfield Ox", 1); } + /** + * If another effect (e.g. Future Sight) allows you to cast nonland cards + * from zones other than your hand, Xmage incorrectly lets you cast those + * cards without paying their mana costs. Omniscience only lets you cast + * spells from your hand without paying their mana costs. + */ + @Test + public void testCastingWithFutureSight() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); + // Play with the top card of your library revealed. + // You may play the top card of your library. + addCard(Zone.BATTLEFIELD, playerA, "Future Sight", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 1); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertTapped("Plains", true); // plains have to be tapped because {2} have to be paid + } + + /** + * If a spell has an additional cost (optional or mandatory, e.g. Entwine), + * Omniscience incorrectly allows you cast the spell as if that cost had + * been paid without paying that spell's mana cost. 117.9d If an alternative + * cost is being paid to cast a spell, any additional costs, cost increases, + * and cost reductions that affect that spell are applied to that + * alternative cost. (See rule 601.2f.) + */ + @Test + public void testCastingWithCyclonicRiftWithOverload() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + // Choose one - Barbed Lightning deals 3 damage to target creature; or Barbed Lightning deals 3 damage to target player. + // Entwine {2} (Choose both if you pay the entwine cost.) + addCard(Zone.HAND, playerA, "Barbed Lightning", 1); + + // Creature - 3/3 Swampwalk + addCard(Zone.BATTLEFIELD, playerB, "Bog Wraith", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning", "Bog Wraith"); + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Barbed Lightning", 1); + assertGraveyardCount(playerB, "Bog Wraith", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + assertTapped("Plains", true); // plains have to be tapped because {2} from Entwine have to be paid + } + + /** + * If a spell has an unpayable cost (e.g. Ancestral Vision, which has no + * mana cost), Omniscience should allow you to cast that spell without + * paying its mana cost. In the case of Ancestral Vision, for example, Xmage + * only gives you the option to suspend Ancestral Vision. 117.6a If an + * unpayable cost is increased by an effect or an additional cost is + * imposed, the cost is still unpayable. If an alternative cost is applied + * to an unpayable cost, including an effect that allows a player to cast a + * spell without paying its mana cost, the alternative cost may be paid. + */ + @Test + public void testCastingUnpayableCost() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); + + // Suspend 4-{U} + // Target player draws three cards. + addCard(Zone.HAND, playerA, "Ancestral Vision", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Vision", playerA); + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Ancestral Vision", 1); + + assertHandCount(playerA, 3); + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MelekIzzetParagonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MelekIzzetParagonTest.java new file mode 100644 index 00000000000..8560a101906 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/MelekIzzetParagonTest.java @@ -0,0 +1,77 @@ +/* + * 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.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class MelekIzzetParagonTest extends CardTestPlayerBase { + + /** + * Wenn Melek, Izzet Paragon liegt und man einen Red/Blue Sun's Zenith von + * der Bib spielt, wird er nicht kopiert, auch wenn der Effekt auf dem Stack + * sichtbar ist. + * + * Meine Theorie ist, dass die Kopie beim in die Bib mischen den Originalen + * nimmt und er daher nicht mehr dem Stack ist um selbst verrechnet zu + * werden + * + */ + @Test + public void testCopyZenith() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + // Play with the top card of your library revealed. + // You may cast the top card of your library if it's an instant or sorcery card. + // Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy. + addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon"); + + // Red Sun's Zenith deals X damage to target creature or player. + // If a creature dealt damage this way would die this turn, exile it instead. + // Shuffle Red Sun's Zenith into its owner's library. + addCard(Zone.LIBRARY, playerA, "Red Sun's Zenith"); // {X}{R} + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Red Sun's Zenith", playerB); + setChoice(playerA, "X=4"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Red Sun's Zenith", 0); + + assertLife(playerA, 20); + assertLife(playerB, 12); + + } +} diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index d7b581515ba..659d4987fb1 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -471,7 +471,10 @@ public abstract class AbilityImpl implements Ability { } // controller specific alternate spell costs if (!noMana && !alternativeCostisUsed) { - if (this.getAbilityType().equals(AbilityType.SPELL)) { + if (this.getAbilityType().equals(AbilityType.SPELL) + // 117.9a Only one alternative cost can be applied to any one spell as it’s being cast. + // So an alternate spell ability can't be paid with Omniscience + && !((SpellAbility) this).getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) { if (alternativeSourceCosts.isAvailable(this, game)) { if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) { diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index 1dedf4b81e7..5106b8dc61c 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -99,10 +99,6 @@ public class SpellAbility extends ActivatedAbilityImpl { && !controllerId.equals(playerId)) { return false; } - // Check if spell has no costs (not {0} mana costs), than it's not castable. E.g. for spells like Living End, that only can be cast by Suspend Ability. - if (this.getManaCosts().isEmpty() && this.getCosts().isEmpty()) { - return false; - } // Check if rule modifying events prevent to cast the spell in check playable mode if (this.isCheckPlayableMode()) { if (game.getContinuousEffects().preventedByRuleModification( diff --git a/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java index 963df6092bc..11d4a2764f3 100644 --- a/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java @@ -2,11 +2,13 @@ package mage.abilities.common; 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; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; /** * "When enchanted/equipped creature dies" triggered ability @@ -17,6 +19,7 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { private String attachedDescription; private boolean diesRuleText; + protected SetTargetPointer setTargetPointer; public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription) { this(effect, attachedDescription, false); @@ -27,15 +30,21 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { } public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional, boolean diesRuleText) { + this(effect, attachedDescription, optional, diesRuleText, SetTargetPointer.NONE); + } + + public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional, boolean diesRuleText, SetTargetPointer setTargetPointer) { super(Zone.ALL, effect, optional); // because the trigger only triggers if the object was attached, it doesn't matter where the Attachment was moved to (e.g. by replacement effect) after the trigger triggered, so Zone.all this.attachedDescription = attachedDescription; this.diesRuleText = diesRuleText; + this.setTargetPointer = setTargetPointer; } public DiesAttachedTriggeredAbility(final DiesAttachedTriggeredAbility ability) { super(ability); this.attachedDescription = ability.attachedDescription; this.diesRuleText = ability.diesRuleText; + this.setTargetPointer = ability.setTargetPointer; } @Override @@ -69,6 +78,16 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { if (triggered) { for (Effect effect : getEffects()) { effect.setValue("attachedTo", zEvent.getTarget()); + if (setTargetPointer.equals(SetTargetPointer.ATTACHED_TO_CONTROLLER)) { + Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); + if (attachment != null && attachment.getAttachedTo() != null) { + Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(), Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter()); + if (attachedTo != null) { + effect.setTargetPointer(new FixedTarget(attachedTo.getControllerId())); + } + } + + } } return true; } diff --git a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java index 5fb7fd20f90..be4893b1b45 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -29,6 +29,7 @@ package mage.abilities.costs; import java.util.Iterator; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.mana.ManaCost; @@ -39,6 +40,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -145,28 +147,39 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } Player player = game.getPlayer(ability.getControllerId()); if (player != null) { - Costs alternativeCosts; + Costs alternativeCostsToCheck; if (dynamicCost != null) { - alternativeCosts = new CostsImpl<>(); - alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(ability, game))); + alternativeCostsToCheck = new CostsImpl<>(); + alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(ability, game))); } else { - alternativeCosts = this.alternateCosts; + alternativeCostsToCheck = this.alternateCosts; } String costChoiceText; if (dynamicCost != null) { costChoiceText = dynamicCost.getText(ability, game); } else { - costChoiceText = alternativeCosts.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCosts.getText() + ")"; + costChoiceText = alternativeCostsToCheck.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCostsToCheck.getText() + ")"; } - if (alternativeCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) + if (alternativeCostsToCheck.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) && player.chooseUse(Outcome.Benefit, costChoiceText, this, game)) { - ability.getManaCostsToPay().clear(); + if (ability instanceof SpellAbility) { + for (Iterator iterator = ability.getManaCostsToPay().iterator(); iterator.hasNext();) { + ManaCost manaCost = iterator.next(); + if (manaCost instanceof VariableCost) { + iterator.remove(); + } + } + CardUtil.reduceCost((SpellAbility) ability, ability.getManaCosts()); + + } else { + ability.getManaCostsToPay().clear(); + } if (!onlyMana) { ability.getCosts().clear(); } - for (Cost cost : alternativeCosts) { + for (Cost cost : alternativeCostsToCheck) { AlternativeCost2 alternateCost = (AlternativeCost2) cost; alternateCost.activate(); for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext();) { @@ -190,14 +203,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter @Override public boolean isActivated(Ability source, Game game) { - Costs alternativeCosts; + Costs alternativeCostsToCheck; if (dynamicCost != null) { - alternativeCosts = new CostsImpl<>(); - alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(source, game))); + alternativeCostsToCheck = new CostsImpl<>(); + alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(source, game))); } else { - alternativeCosts = this.alternateCosts; + alternativeCostsToCheck = this.alternateCosts; } - for (AlternativeCost2 cost : alternativeCosts) { + for (AlternativeCost2 cost : alternativeCostsToCheck) { if (cost.isActivated(game)) { return true; } diff --git a/Mage/src/mage/abilities/effects/RedirectionEffect.java b/Mage/src/mage/abilities/effects/RedirectionEffect.java index 6c339460edf..7979ad8f1b1 100644 --- a/Mage/src/mage/abilities/effects/RedirectionEffect.java +++ b/Mage/src/mage/abilities/effects/RedirectionEffect.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,12 +20,11 @@ * 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; import mage.abilities.Ability; @@ -46,36 +45,67 @@ import mage.target.Target; public abstract class RedirectionEffect extends ReplacementEffectImpl { protected Target redirectTarget; + protected int amountToRedirect; + protected boolean oneUsage; public RedirectionEffect(Duration duration) { + this(duration, Integer.MAX_VALUE, false); + } + + public RedirectionEffect(Duration duration, int amountToRedirect, boolean oneUsage) { super(duration, Outcome.RedirectDamage); this.effectType = EffectType.REDIRECTION; + this.amountToRedirect = amountToRedirect; + this.oneUsage = oneUsage; } public RedirectionEffect(final RedirectionEffect effect) { super(effect); this.redirectTarget = effect.redirectTarget; + this.amountToRedirect = effect.amountToRedirect; + this.oneUsage = effect.oneUsage; } @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent)event; - Permanent permanent = game.getPermanent(redirectTarget.getFirstTarget()); - if (permanent != null) { - permanent.damage(damageEvent.getAmount(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - return true; - } - Player player = game.getPlayer(redirectTarget.getFirstTarget()); - if (player != null) { - player.damage(damageEvent.getAmount(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - return true; + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; } return false; } + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + String sourceLogName = source != null ? game.getObject(source.getSourceId()).getLogName() + ": " : ""; + DamageEvent damageEvent = (DamageEvent) event; + int restDamage = 0; + int damageToRedirect = event.getAmount(); + if (damageEvent.getAmount() > amountToRedirect) { + restDamage = damageEvent.getAmount() - amountToRedirect; + damageToRedirect = amountToRedirect; + } + if (damageToRedirect > 0 && oneUsage) { + this.discard(); + } + Permanent permanent = game.getPermanent(redirectTarget.getFirstTarget()); + if (permanent != null) { + permanent.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); + game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + permanent.getLogName()); + } else { + Player player = game.getPlayer(redirectTarget.getFirstTarget()); + if (player != null) { + player.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); + game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + player.getLogName()); + } + } + if (restDamage > 0) { + damageEvent.setAmount(restDamage); + return false; + } + return true; + } + } diff --git a/Mage/src/mage/abilities/effects/common/NameACardEffect.java b/Mage/src/mage/abilities/effects/common/NameACardEffect.java index 1a642fac0f0..7cf32682fa6 100644 --- a/Mage/src/mage/abilities/effects/common/NameACardEffect.java +++ b/Mage/src/mage/abilities/effects/common/NameACardEffect.java @@ -73,7 +73,7 @@ public class NameACardEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getPermanentEntering(source.getSourceId()); if (sourceObject == null) { - game.getObject(source.getSourceId()); + sourceObject = game.getObject(source.getSourceId()); } if (controller != null && sourceObject != null) { Choice cardChoice = new ChoiceImpl(); diff --git a/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java b/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java new file mode 100644 index 00000000000..61b35d63abd --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.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.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.RedirectionEffect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LevelX2 + */ +public class RedirectDamageFromSourceToTargetEffect extends RedirectionEffect { + + public RedirectDamageFromSourceToTargetEffect(Duration duration, int amountToRedirect, boolean oneUsage) { + super(duration, amountToRedirect, oneUsage); + staticText = "The next " + amountToRedirect + " damage that would be dealt to {this} this turn is dealt to target creature you control instead."; + } + + public RedirectDamageFromSourceToTargetEffect(final RedirectDamageFromSourceToTargetEffect effect) { + super(effect); + } + + @Override + public RedirectDamageFromSourceToTargetEffect copy() { + return new RedirectDamageFromSourceToTargetEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getSourceId())) { + this.redirectTarget = source.getTargets().get(0); + return true; + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java b/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java index 47b4322115a..fa8e72af499 100644 --- a/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java @@ -1,37 +1,35 @@ /* -* 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; import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -59,15 +57,15 @@ public class ShuffleSpellEffect extends OneShotEffect implements MageSingleton { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Spell spell = game.getStack().getSpell(source.getSourceId()); + // We have to use the spell id because in case of copied spells, the sourceId can be multiple times on the stack + Spell spell = game.getStack().getSpell(source.getId()); if (spell != null) { - Card spellCard = spell.getCard(); - if (spellCard != null) { - Player owner = game.getPlayer(spellCard.getOwnerId()); + if (controller.moveCards(spell, Zone.LIBRARY, source, game) && !spell.isCopy()) { + Player owner = game.getPlayer(spell.getCard().getOwnerId()); if (owner != null) { - controller.moveCardToLibraryWithInfo(spellCard, source.getSourceId(), game, Zone.STACK, true, true); owner.shuffleLibrary(game); } + } } return true; diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java index d523a5cbd06..1c2e3dbc87a 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java @@ -36,7 +36,6 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -75,19 +74,17 @@ public class SetPowerToughnessSourceEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - MageObject mageObject = game.getObject(source.getSourceId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); if (mageObject == null) { - game.getPermanentEntering(source.getSourceId()); + if (duration.equals(Duration.Custom) || isTemporary()) { + mageObject = game.getPermanent(source.getSourceId()); + } else { + mageObject = game.getObject(source.getSourceId()); + } } if (mageObject == null) { - if (duration.equals(Duration.Custom)) { - discard(); - } - return false; - } else if (isTemporary()) { // it's somehow w - if (!(mageObject instanceof Permanent)) { - return false; - } + discard(); + return true; } if (amount != null) { int value = amount.calculate(game, source, this); diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java index c92cbb7540e..de9c442f289 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java @@ -90,7 +90,7 @@ public class SearchLibraryPutInPlayEffect extends SearchEffect { if (player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { player.moveCards(new CardsImpl(target.getTargets()).getCards(game), - Zone.BATTLEFIELD, source, game, true, false, false, null); + Zone.BATTLEFIELD, source, game, tapped, false, false, null); } player.shuffleLibrary(game); return true; diff --git a/Mage/src/mage/abilities/keyword/EntwineAbility.java b/Mage/src/mage/abilities/keyword/EntwineAbility.java index c81d1cbd955..0604445c2a4 100644 --- a/Mage/src/mage/abilities/keyword/EntwineAbility.java +++ b/Mage/src/mage/abilities/keyword/EntwineAbility.java @@ -111,7 +111,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM if (player != null) { this.resetCosts(); if (additionalCost != null) { - if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) { + if (player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) { additionalCost.activate(); for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); diff --git a/Mage/src/mage/constants/SetTargetPointer.java b/Mage/src/mage/constants/SetTargetPointer.java index 6aeca5e026d..9f4acc8876d 100644 --- a/Mage/src/mage/constants/SetTargetPointer.java +++ b/Mage/src/mage/constants/SetTargetPointer.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.constants; /** @@ -33,5 +32,6 @@ package mage.constants; * @author LevelX2 */ public enum SetTargetPointer { - NONE, PLAYER, SPELL, CARD, PERMANENT; + + NONE, PLAYER, SPELL, CARD, PERMANENT, ATTACHED_TO_CONTROLLER; } diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java index f7537638758..907e0cc8526 100644 --- a/Mage/src/mage/counters/CounterType.java +++ b/Mage/src/mage/counters/CounterType.java @@ -53,6 +53,7 @@ public enum CounterType { DOOM("doom"), ELIXIR("elixir"), EON("eon"), + EXPERIENCE("experience"), EYEBALL("eyeball"), FADE("fade"), FATE("fate"), diff --git a/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.java b/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.java new file mode 100644 index 00000000000..cd906f4a2ac --- /dev/null +++ b/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.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.filter.predicate.mageobject; + +import mage.MageObject; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.Target; + +/** + * All targets that are already selected in other target definitions of the + * source are omitted To use this predicate you have to set the targetTag of all + * targets involved in the card constructor to a unique value (e.g. using 1,2,3 + * for three targets) + * + * @author LevelX2 + */ +public class AnotherTargetPredicate implements ObjectSourcePlayerPredicate> { + + private final int targetTag; + + public AnotherTargetPredicate(int targetTag) { + this.targetTag = targetTag; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject source = game.getStack().getStackObject(input.getSourceId()); + if (source != null) { + for (Target target : source.getStackAbility().getTargets()) { + if (target.getTargetTag() > 0 // target is included in the target group to check + && target.getTargetTag() != targetTag // it's not the target of this predicate + && target.getTargets().contains(input.getObject().getId())) { // if the uuid already is used for another target in the group it's no allowed here + return false; + } + } + } + return true; + } + + @Override + public String toString() { + return "Another target"; + } +} diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 83b99d90d46..0b0f22fb45a 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -622,7 +622,8 @@ public class Spell extends StackObjImpl implements Card { } public Spell copySpell() { - return new Spell(this.card.copy(), this.ability.copySpell(), this.controllerId, this.fromZone); + // replaced card.copy by copy (card content should no longer be changed) + return new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone); } @Override diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 80129822440..cdeb49c91a4 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1217,11 +1217,15 @@ public abstract class PlayerImpl implements Player, Serializable { public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); + ManaOptions availableMana = null; +// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly +// availableMana.addMana(manaPool.getMana()); for (Ability ability : object.getAbilities()) { if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) { if (ability.getZone().match(zone)) { if (ability instanceof ActivatedAbility) { - if (((ActivatedAbility) ability).canActivate(playerId, game)) { + if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) { +// if (((ActivatedAbility) ability).canActivate(playerId, game)) { useable.put(ability.getId(), (ActivatedAbility) ability); } } else if (ability instanceof AlternativeSourceCosts) { @@ -2297,6 +2301,14 @@ public abstract class PlayerImpl implements Player, Serializable { return result; } + /** + * + * @param ability + * @param available if null, it won't be checked if enough mana is available + * @param sourceObject + * @param game + * @return + */ protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) { if (!(ability instanceof ManaAbility)) { ActivatedAbility copy = ability.copy(); @@ -2317,15 +2329,26 @@ public abstract class PlayerImpl implements Player, Serializable { } } } - - ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions(); - if (abilityOptions.size() == 0) { - return true; - } else { - for (Mana mana : abilityOptions) { - for (Mana avail : available) { - if (mana.enough(avail)) { - return true; + boolean canBeCastRegularly = true; + if (copy instanceof SpellAbility && copy.getManaCosts().isEmpty() && copy.getCosts().isEmpty()) { + // 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost... + // 117.6a (...) If an alternative cost is applied to an unpayable cost, + // including an effect that allows a player to cast a spell without paying its mana cost, the alternative cost may be paid. + canBeCastRegularly = false; + } + if (canBeCastRegularly) { + ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions(); + if (abilityOptions.size() == 0) { + return true; + } else { + if (available == null) { + return true; + } + for (Mana mana : abilityOptions) { + for (Mana avail : available) { + if (mana.enough(avail)) { + return true; + } } } } @@ -2369,6 +2392,9 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.size() == 0) { return true; } else { + if (available == null) { + return true; + } for (Mana mana : manaCosts.getOptions()) { for (Mana avail : available) { if (mana.enough(avail)) { @@ -3097,7 +3123,11 @@ public abstract class PlayerImpl implements Player, Serializable { break; case LIBRARY: for (Card card : cards) { - fromZone = game.getState().getZone(card.getId()); + if (card instanceof Spell) { + fromZone = game.getState().getZone(((Spell) card).getSourceId()); + } else { + fromZone = game.getState().getZone(card.getId()); + } boolean hideCard = fromZone.equals(Zone.HAND) || fromZone.equals(Zone.LIBRARY); if (moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, !hideCard)) { successfulMovedCards.add(card); diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index b6bd637b1e4..80b977f4eed 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -151,4 +151,8 @@ public interface Target extends Serializable { UUID getAbilityController(); Player getTargetController(Game game, UUID playerId); + + int getTargetTag(); + + void setTargetTag(int tag); } diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index ce0a8790f48..df09789ca13 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -70,6 +70,8 @@ public abstract class TargetImpl implements Target { protected UUID targetController = null; // if null the ability controller is the targetController protected UUID abilityController = null; // only used if target controller != ability controller + protected int targetTag; // can be set if other target check is needed (AnotherTargetPredicate) + @Override public abstract TargetImpl copy(); @@ -95,6 +97,7 @@ public abstract class TargetImpl implements Target { this.notTarget = target.notTarget; this.targetController = target.targetController; this.abilityController = target.abilityController; + this.targetTag = target.targetTag; } @Override @@ -545,4 +548,20 @@ public abstract class TargetImpl implements Target { return requiredExplicitlySet; } + @Override + public int getTargetTag() { + return targetTag; + } + + /** + * Is used to be able to check, that another target is slected within the + * group of targets of the ability with a target tag > 0. + * + * @param targetTag + */ + @Override + public void setTargetTag(int targetTag) { + this.targetTag = targetTag; + } + } diff --git a/Mage/src/mage/target/TargetPermanent.java b/Mage/src/mage/target/TargetPermanent.java index 222e08d0586..068bac9f0cb 100644 --- a/Mage/src/mage/target/TargetPermanent.java +++ b/Mage/src/mage/target/TargetPermanent.java @@ -1,44 +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.target; import java.util.HashSet; import java.util.Set; import java.util.UUID; -import mage.constants.Zone; import mage.MageObject; import mage.abilities.Ability; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; - /** * * @author BetaSteward_at_googlemail.com @@ -50,7 +48,7 @@ public class TargetPermanent extends TargetObject { public TargetPermanent() { this(1, 1, new FilterPermanent(), false); } - + public TargetPermanent(FilterPermanent filter) { this(1, 1, filter, false); } @@ -88,8 +86,8 @@ public class TargetPermanent extends TargetObject { // first for protection from spells or abilities (e.g. protection from colored spells, r1753) // second for protection from sources (e.g. protection from artifacts + equip ability) if (!isNotTarget()) { - if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, game) || - !permanent.canBeTargetedBy(game.getObject(source.getSourceId()), controllerId, game)) { + if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, game) + || !permanent.canBeTargetedBy(game.getObject(source.getSourceId()), controllerId, game)) { return false; } } @@ -117,7 +115,8 @@ public class TargetPermanent extends TargetObject { /** * Checks if there are enough {@link Permanent} that can be chosen. * - * Takes into account notTarget parameter, in case it's true doesn't check for protection, shroud etc. + * Takes into account notTarget parameter, in case it's true doesn't check + * for protection, shroud etc. * * @param sourceId the target event source * @param sourceControllerId controller of the target event source @@ -132,7 +131,7 @@ public class TargetPermanent extends TargetObject { } int count = 0; MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { if (!targets.containsKey(permanent.getId())) { if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { count++; @@ -146,9 +145,10 @@ public class TargetPermanent extends TargetObject { } /** - * Checks if there are enough {@link Permanent} that can be selected. Should not be used - * for Ability targets since this does not check for protection, shroud etc. - * + * Checks if there are enough {@link Permanent} that can be selected. Should + * not be used for Ability targets since this does not check for protection, + * shroud etc. + * * @param sourceControllerId - controller of the select event * @param game * @return - true if enough valid {@link Permanent} exist @@ -162,7 +162,7 @@ public class TargetPermanent extends TargetObject { return true; } int count = 0; - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (!targets.containsKey(permanent.getId())) { count++; if (count >= remainingTargets) { @@ -177,7 +177,7 @@ public class TargetPermanent extends TargetObject { public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { if (!targets.containsKey(permanent.getId())) { if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); @@ -190,7 +190,7 @@ public class TargetPermanent extends TargetObject { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (!targets.containsKey(permanent.getId())) { possibleTargets.add(permanent.getId()); } diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index 9b462e003ae..a63078e0d91 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -14,6 +14,7 @@ Classic Sixth Edition|classicsixthedition| Coldsnap|coldsnap| Commander 2013 Edition|commander2013| Commander 2014 Edition|commander2014| +Commander 2015|commander2015| Conflux|conflux| Dark Ascension|darkascension| Darksteel|darksteel| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 9146198e3b3..5845fd2d74b 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -27631,3 +27631,5 @@ Swamp|Battle for Zendikar|262|L||Basic Land - Swamp|||({t}: Add {B} to your m Swamp|Battle for Zendikar|264|L||Basic Land - Swamp|||({t}: Add {B} to your mana pool.)| Swamp|Battle for Zendikar|260|L||Basic Land - Swamp|||({t}: Add {B} to your mana pool.)| Swamp|Battle for Zendikar|261|L||Basic Land - Swamp|||({t}: Add {B} to your mana pool.)| +Eternal Witness|Commander 2015|183|U|{1}{G}{G}|Creature - Human Shaman|2|1|When Eternal Witness enters the battlefield, you may return target card from your graveyard to your hand.| +Kalemne, Disciple of Iroas|Commander 2015|999|M|{2}{R}{W}|Legendary Creature - Giant Soldier|3|3|Double strike, vigilance$Whenever you cast a creature spell with converted mana cost 5 or greater, you get an experience counter.$Kalemne, Disciple of Iroas gets +1/+1 for each experience counter you have.| \ No newline at end of file diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 83e479e4585..83318ee3973 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -25,6 +25,7 @@ Chronicles|CHR| Clash Pack|CLASH| Commander 2013 Edition|C13| Commander 2014 Edition|C14| +Commander 2015|C15| Conflux|CON| Coldsnap|CSP| Dark Ascension|DKA| diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index f922aab6fba..bd7cacbd8a5 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -36,6 +36,9 @@ git log 7650f53dee0b4d480d2a63befed72b6c8197e752..head --diff-filter=A --name-st since 1.4.4.v8 git log 8c7dc7b2da3630b6dfec1390854fa2be11631c79..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.4.v9 +git log 1b71f505064b82893003207fc29954de533fbed5..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt + 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: > perl extract_in_wiki_format.perl