diff --git a/Mage.Sets/src/mage/cards/g/Glamdring.java b/Mage.Sets/src/mage/cards/g/Glamdring.java new file mode 100644 index 00000000000..aba45ab7283 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Glamdring.java @@ -0,0 +1,95 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * + * @author bobby-mccann + */ +public final class Glamdring extends CardImpl { + + public Glamdring(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has first strike and gets +1/+0 for each instant and sorcery card in your graveyard. + Effect firstStrike = new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT) + .setText("Equipped creature has first strike"); + Effect boost = new BoostEquippedEffect( + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY), StaticValue.get(0)) + .setText(" and gets +1/+0 for each instant and sorcery card in your graveyard."); + + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, firstStrike); + ability.addEffect(boost); + this.addAbility(ability); + + // Whenever equipped creature deals combat damage to a player, you may cast an instant or sorcery spell from your hand with mana value less than or equal to that damage without paying its mana cost. + this.addAbility( + new DealsDamageToAPlayerAttachedTriggeredAbility( + new GlamdringEffect(), + "equipped creature", false + ) + ); + + // Equip {3} + this.addAbility(new EquipAbility(3, false)); + } + + private Glamdring(final Glamdring card) { + super(card); + } + + @Override + public Glamdring copy() { + return new Glamdring(this); + } +} + +class GlamdringEffect extends OneShotEffect { + GlamdringEffect() { + super(Outcome.Benefit); + this.staticText = "you may cast an instant or sorcery spell from your hand with mana value less than or equal to that damage without paying its mana cost"; + } + + private GlamdringEffect(final GlamdringEffect effect) { + super(effect); + } + + @Override + public GlamdringEffect copy() { + return new GlamdringEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery spell from your hand with mana value less than or equal to that damage"); + filter.add(new ManaValuePredicate( + ComparisonType.OR_LESS, SavedDamageValue.MUCH.calculate(game, source, this) + )); + return new CastFromHandForFreeEffect(filter).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java index 8bd73ebcc69..d9fe8fc62a7 100644 --- a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java +++ b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java @@ -115,6 +115,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Gimli's Fury", 131, Rarity.COMMON, mage.cards.g.GimlisFury.class)); cards.add(new SetCardInfo("Gimli, Counter of Kills", 129, Rarity.UNCOMMON, mage.cards.g.GimliCounterOfKills.class)); cards.add(new SetCardInfo("Gimli, Mournful Avenger", 209, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class)); + cards.add(new SetCardInfo("Glamdring", 239, Rarity.MYTHIC, mage.cards.g.Glamdring.class)); cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 132, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class)); cards.add(new SetCardInfo("Glorious Gale", 51, Rarity.COMMON, mage.cards.g.GloriousGale.class)); cards.add(new SetCardInfo("Goblin Assailant", 295, Rarity.COMMON, mage.cards.g.GoblinAssailant.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/GlamdringTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/GlamdringTest.java new file mode 100644 index 00000000000..030bdfe7d6b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/GlamdringTest.java @@ -0,0 +1,81 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import static org.junit.Assert.fail; + +public class GlamdringTest extends CardTestPlayerBase { + void setUp() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.HAND, playerA, "Healing Salve"); + addCard(Zone.HAND, playerA, "In Garruk's Wake"); + addCard(Zone.HAND, playerA, "Blue Sun's Zenith"); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + addCard(Zone.BATTLEFIELD, playerA, "Glamdring"); + addCard(Zone.BATTLEFIELD, playerA, "Blur Sliver"); // 2/2, Haste + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Blur Sliver"); + } + @Test + public void tooExpensiveToCastForFree() { + setUp(); + + attack(1, playerA, "Blur Sliver"); + setChoice(playerA, "In Garruk's Wake"); // 9 mana, so we shouldn't be able to choose it + setChoice(playerA, "Yes"); + + try { + setStopAt(1, PhaseStep.FIRST_COMBAT_DAMAGE); + execute(); + } + catch (AssertionError e) { + assert(e.getMessage().contains("Missing CHOICE def for turn 1, step FIRST_COMBAT_DAMAGE, PlayerA")); + return; + } + fail("Was able to pick [[In Garruk's Wake]] but it costs more than 2"); + } + + @Test + public void canCastForFree() { + setUp(); + + attack(1, playerA, "Blur Sliver"); + setChoice(playerA, "Lightning Bolt"); // 1 mana, so we should be able to choose it + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); // target player + + setStopAt(1, PhaseStep.FIRST_COMBAT_DAMAGE); + execute(); + + // Lightning Bolt did 3 damage + assertLife(playerB, 20 - 2 - 3); + } + + @Test + public void enoughCardsInGraveyardToCastInGarruksWake() { + setUp(); + + addCard(Zone.GRAVEYARD, playerA, "Serum Visions", 7); + addCard(Zone.GRAVEYARD, playerA, "Brainstorm", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elder Deep-Fiend", 2, true); + attack(1, playerA, "Blur Sliver"); + setChoice(playerA, "In Garruk's Wake"); // We now do enough damage to cast it + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.FIRST_COMBAT_DAMAGE); + execute(); + + // Equipped creature gets +1/+0 for each instant and sorcery card in your graveyard + assertLife(playerB, 20 - 2 - (7 + 5)); + + // In Garruk's Wake destroyed our opponent's creatures + assertPermanentCount(playerB, "Elder Deep-Fiend", 0); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentsComparedToOpponentsCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentsComparedToOpponentsCondition.java index 2124fb47d71..5a9e308f3b3 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentsComparedToOpponentsCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentsComparedToOpponentsCondition.java @@ -39,12 +39,14 @@ public class ControlsPermanentsComparedToOpponentsCondition implements Condition @Override public String toString() { - StringBuilder sb = new StringBuilder("if you control "); - sb.append(type.getText1()).append(" "); - sb.append(filterPermanent.getMessage()).append(" "); - sb.append(type.getText2()); - sb.append(" each opponent"); - return sb.toString(); + switch (type) { + case EQUAL_TO: return String.format("equal %s to each opponent", filterPermanent.getMessage()); + case FEWER_THAN: return String.format("fewer %s than each opponent", filterPermanent.getMessage()); + case MORE_THAN: return String.format("more %s than each opponent", filterPermanent.getMessage()); + case OR_LESS: return String.format("fewer or equal %s than each opponent", filterPermanent.getMessage()); + case OR_GREATER: return String.format("more or equal %s than each opponent", filterPermanent.getMessage()); + default: throw new IllegalArgumentException("comparison rules for " + type + " missing"); + } } } diff --git a/Mage/src/main/java/mage/constants/ComparisonType.java b/Mage/src/main/java/mage/constants/ComparisonType.java index cbbeb739114..7b01b9b126d 100644 --- a/Mage/src/main/java/mage/constants/ComparisonType.java +++ b/Mage/src/main/java/mage/constants/ComparisonType.java @@ -4,18 +4,16 @@ package mage.constants; * Created by IGOUDT on 5-3-2017. */ public enum ComparisonType { - FEWER_THAN("<", "fewer", "than"), - EQUAL_TO("==", "equal", "to"), - MORE_THAN(">", "more", "than"); + FEWER_THAN("<"), + OR_LESS("<="), + EQUAL_TO("=="), + MORE_THAN(">"), + OR_GREATER(">="); - String operator; - String text1; - String text2; + final String operator; - ComparisonType(String op, String text1, String text2) { + ComparisonType(String op) { this.operator = op; - this.text1 = text1; - this.text2 = text2; } @Override @@ -23,14 +21,6 @@ public enum ComparisonType { return operator; } - public String getText1() { - return text1; - } - - public String getText2() { - return text2; - } - public static boolean compare(int source, ComparisonType comparison, int target) { switch (comparison) { case MORE_THAN: @@ -39,6 +29,10 @@ public enum ComparisonType { return source < target; case EQUAL_TO: return source == target; + case OR_GREATER: + return source >= target; + case OR_LESS: + return source <= target; default: throw new IllegalArgumentException("comparison rules for " + comparison + " missing"); }