mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
implement [MH3] Unstable Amulet
This commit is contained in:
parent
83ad5d26dc
commit
b35924dc4c
4 changed files with 252 additions and 0 deletions
164
Mage.Sets/src/mage/cards/u/UnstableAmulet.java
Normal file
164
Mage.Sets/src/mage/cards/u/UnstableAmulet.java
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package mage.cards.u;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.costs.common.PayEnergyCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||
import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.card.CastFromZonePredicate;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class UnstableAmulet extends CardImpl {
|
||||
|
||||
private static final FilterSpell filter = new FilterSpell("a spell from anywhere other than your hand");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.not(new CastFromZonePredicate(Zone.HAND)));
|
||||
}
|
||||
|
||||
public UnstableAmulet(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{R}");
|
||||
|
||||
// When Unstable Amulet enters the battlefield, you get {E}{E}.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
|
||||
|
||||
// Whenever you cast a spell from anywhere other than your hand, Unstable Amulet deals 1 damage to each opponent.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new DamagePlayersEffect(1, TargetController.OPPONENT),
|
||||
filter, false
|
||||
));
|
||||
|
||||
// {T}, Pay {E}{E}: Exile the top card of your library. You may play it until you exile another card with Unstable Amulet.
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new UnstableAmuletEffect(),
|
||||
new TapSourceCost()
|
||||
);
|
||||
ability.addCost(new PayEnergyCost(2));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private UnstableAmulet(final UnstableAmulet card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnstableAmulet copy() {
|
||||
return new UnstableAmulet(this);
|
||||
}
|
||||
}
|
||||
|
||||
class UnstableAmuletEffect extends OneShotEffect {
|
||||
|
||||
UnstableAmuletEffect() {
|
||||
super(Outcome.DrawCard);
|
||||
staticText = "Exile the top card of your library. You may play it until you exile another card with {this}.";
|
||||
}
|
||||
|
||||
private UnstableAmuletEffect(final UnstableAmuletEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnstableAmuletEffect copy() {
|
||||
return new UnstableAmuletEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null || !controller.getLibrary().hasCards()) {
|
||||
return false;
|
||||
}
|
||||
Card card = controller.getLibrary().getFromTop(game);
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source);
|
||||
String exileName = CardUtil.getSourceIdName(game, source);
|
||||
controller.moveCardsToExile(card, source, game, true, exileId, exileName);
|
||||
game.getState().processAction(game);
|
||||
if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) {
|
||||
return true;
|
||||
}
|
||||
// Allow the card to be played until it leaves that exile zone.
|
||||
ContinuousEffect effect = new UnstableAmuletPlayEffect(exileId);
|
||||
effect.setTargetPointer(new FixedTarget(card, game));
|
||||
game.addEffect(effect, source);
|
||||
// Clean the exile Zone from other cards, that can no longer be played.
|
||||
ExileZone exileZone = game.getExile().getExileZone(exileId);
|
||||
if (exileZone == null) {
|
||||
return true;
|
||||
}
|
||||
Set<Card> inExileZone = exileZone.getCards(game);
|
||||
for (Card cardInExile : inExileZone) {
|
||||
if (cardInExile.getId().equals(card.getId())) {
|
||||
continue;
|
||||
}
|
||||
game.getExile().moveToMainExileZone(cardInExile, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class UnstableAmuletPlayEffect extends AsThoughEffectImpl {
|
||||
|
||||
// The exile zone the card should be in for the effect to work.
|
||||
private final UUID exileId;
|
||||
|
||||
UnstableAmuletPlayEffect(UUID exileId) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
|
||||
this.exileId = exileId;
|
||||
}
|
||||
|
||||
private UnstableAmuletPlayEffect(final UnstableAmuletPlayEffect effect) {
|
||||
super(effect);
|
||||
this.exileId = effect.exileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnstableAmuletPlayEffect copy() {
|
||||
return new UnstableAmuletPlayEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
UUID cardId = getTargetPointer().getFirst(game, source);
|
||||
if (cardId == null) {
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
ExileZone exileZone = game.getExile().getExileZone(exileId);
|
||||
if (exileZone == null || !exileZone.contains(cardId)) {
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
return cardId.equals(objectId) && affectedControllerId.equals(source.getControllerId());
|
||||
}
|
||||
}
|
||||
|
|
@ -285,6 +285,7 @@ public final class ModernHorizons3 extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ugin's Labyrinth", 233, Rarity.MYTHIC, mage.cards.u.UginsLabyrinth.class));
|
||||
cards.add(new SetCardInfo("Ulamog, the Defiler", 15, Rarity.MYTHIC, mage.cards.u.UlamogTheDefiler.class));
|
||||
cards.add(new SetCardInfo("Unfathomable Truths", 77, Rarity.COMMON, mage.cards.u.UnfathomableTruths.class));
|
||||
cards.add(new SetCardInfo("Unstable Amulet", 142, Rarity.UNCOMMON, mage.cards.u.UnstableAmulet.class));
|
||||
cards.add(new SetCardInfo("Urza's Cave", 234, Rarity.UNCOMMON, mage.cards.u.UrzasCave.class));
|
||||
cards.add(new SetCardInfo("Urza's Incubator", 297, Rarity.RARE, mage.cards.u.UrzasIncubator.class));
|
||||
cards.add(new SetCardInfo("Utter Insignificance", 78, Rarity.COMMON, mage.cards.u.UtterInsignificance.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package org.mage.test.cards.single.mh3;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class UnstableAmuletTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.u.UnstableAmulet Unstable Amulet} {1}{R}
|
||||
* Artifact
|
||||
* When Unstable Amulet enters the battlefield, you get {E}{E} (two energy counters).
|
||||
* Whenever you cast a spell from anywhere other than your hand, Unstable Amulet deals 1 damage to each opponent.
|
||||
* {T}, Pay {E}{E}: Exile the top card of your library. You may play it until you exile another card with Unstable Amulet.
|
||||
*/
|
||||
private static final String amulet = "Unstable Amulet";
|
||||
|
||||
@Test
|
||||
public void test_Simple() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Taiga", 2);
|
||||
addCard(Zone.HAND, playerA, amulet);
|
||||
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, amulet, true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Pay {E}{E}");
|
||||
|
||||
checkPlayableAbility("No mana to cast Bears", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Grizzly Bears", false);
|
||||
checkExileCount("Bears in exile", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1);
|
||||
|
||||
checkPlayableAbility("Can play Bears", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
|
||||
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 1); // 1 damage from Amulet trigger
|
||||
assertPermanentCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ExileAnother_EndPreviousPlay() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Taiga", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||
addCard(Zone.HAND, playerA, amulet);
|
||||
addCard(Zone.HAND, playerA, "Tune the Narrative"); // Draw a card. You get {E}{E}
|
||||
addCard(Zone.LIBRARY, playerA, "Balduvian Bears");
|
||||
addCard(Zone.LIBRARY, playerA, "Plains", 2); // draw for turn 3 + Tune the Narrative
|
||||
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, amulet, true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Pay {E}{E}");
|
||||
|
||||
checkPlayableAbility("1: No mana to cast Grizzly Bears", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Grizzly Bears", false);
|
||||
checkExileCount("1: Bears in exile", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1);
|
||||
|
||||
checkPlayableAbility("2: Can play Grizzly Bears", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Tune the Narrative", true);
|
||||
|
||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Pay {E}{E}");
|
||||
checkExileCount("3: Grizzly Bears in exile", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1);
|
||||
checkExileCount("3: Balduvian Bears in exile", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||
checkPlayableAbility("3: Can no longer play Grizzly Bears", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Grizzly Bears", false);
|
||||
checkPlayableAbility("3: Can play Balduvian Bears", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Balduvian Bears", true);
|
||||
castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 1); // 1 damage from Amulet trigger
|
||||
assertExileCount(playerA, "Grizzly Bears", 1);
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -117,6 +117,10 @@ public class Exile implements Serializable, Copyable<Exile> {
|
|||
exileZone.add(card);
|
||||
}
|
||||
|
||||
public void moveToMainExileZone(Card card, Game game) {
|
||||
moveToAnotherZone(card, game, getExileZone(PERMANENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exile copy() {
|
||||
return new Exile(this);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue