mirror of
https://github.com/magefree/mage.git
synced 2025-12-23 12:02:01 -08:00
[LTR] Implement Glamdring (#10574)
This commit is contained in:
parent
cbd5651f80
commit
09530f1cdb
5 changed files with 196 additions and 23 deletions
95
Mage.Sets/src/mage/cards/g/Glamdring.java
Normal file
95
Mage.Sets/src/mage/cards/g/Glamdring.java
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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'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, 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("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("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("Glorious Gale", 51, Rarity.COMMON, mage.cards.g.GloriousGale.class));
|
||||||
cards.add(new SetCardInfo("Goblin Assailant", 295, Rarity.COMMON, mage.cards.g.GoblinAssailant.class));
|
cards.add(new SetCardInfo("Goblin Assailant", 295, Rarity.COMMON, mage.cards.g.GoblinAssailant.class));
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,12 +39,14 @@ public class ControlsPermanentsComparedToOpponentsCondition implements Condition
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder("if you control ");
|
switch (type) {
|
||||||
sb.append(type.getText1()).append(" ");
|
case EQUAL_TO: return String.format("equal %s to each opponent", filterPermanent.getMessage());
|
||||||
sb.append(filterPermanent.getMessage()).append(" ");
|
case FEWER_THAN: return String.format("fewer %s than each opponent", filterPermanent.getMessage());
|
||||||
sb.append(type.getText2());
|
case MORE_THAN: return String.format("more %s than each opponent", filterPermanent.getMessage());
|
||||||
sb.append(" each opponent");
|
case OR_LESS: return String.format("fewer or equal %s than each opponent", filterPermanent.getMessage());
|
||||||
return sb.toString();
|
case OR_GREATER: return String.format("more or equal %s than each opponent", filterPermanent.getMessage());
|
||||||
|
default: throw new IllegalArgumentException("comparison rules for " + type + " missing");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,16 @@ package mage.constants;
|
||||||
* Created by IGOUDT on 5-3-2017.
|
* Created by IGOUDT on 5-3-2017.
|
||||||
*/
|
*/
|
||||||
public enum ComparisonType {
|
public enum ComparisonType {
|
||||||
FEWER_THAN("<", "fewer", "than"),
|
FEWER_THAN("<"),
|
||||||
EQUAL_TO("==", "equal", "to"),
|
OR_LESS("<="),
|
||||||
MORE_THAN(">", "more", "than");
|
EQUAL_TO("=="),
|
||||||
|
MORE_THAN(">"),
|
||||||
|
OR_GREATER(">=");
|
||||||
|
|
||||||
String operator;
|
final String operator;
|
||||||
String text1;
|
|
||||||
String text2;
|
|
||||||
|
|
||||||
ComparisonType(String op, String text1, String text2) {
|
ComparisonType(String op) {
|
||||||
this.operator = op;
|
this.operator = op;
|
||||||
this.text1 = text1;
|
|
||||||
this.text2 = text2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -23,14 +21,6 @@ public enum ComparisonType {
|
||||||
return operator;
|
return operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText1() {
|
|
||||||
return text1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getText2() {
|
|
||||||
return text2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean compare(int source, ComparisonType comparison, int target) {
|
public static boolean compare(int source, ComparisonType comparison, int target) {
|
||||||
switch (comparison) {
|
switch (comparison) {
|
||||||
case MORE_THAN:
|
case MORE_THAN:
|
||||||
|
|
@ -39,6 +29,10 @@ public enum ComparisonType {
|
||||||
return source < target;
|
return source < target;
|
||||||
case EQUAL_TO:
|
case EQUAL_TO:
|
||||||
return source == target;
|
return source == target;
|
||||||
|
case OR_GREATER:
|
||||||
|
return source >= target;
|
||||||
|
case OR_LESS:
|
||||||
|
return source <= target;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("comparison rules for " + comparison + " missing");
|
throw new IllegalArgumentException("comparison rules for " + comparison + " missing");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue