[LTR] Implement Glamdring (#10574)

This commit is contained in:
Bobby McCann 2023-07-13 00:39:31 +01:00 committed by GitHub
parent cbd5651f80
commit 09530f1cdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 196 additions and 23 deletions

View 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);
}
}

View file

@ -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));

View file

@ -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);
}
}

View file

@ -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");
}
}
}

View file

@ -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");
}