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, 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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue