mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 02:52:02 -08:00
implement [MH3] Ulamog, the Defiler
This commit is contained in:
parent
4cb93b6d36
commit
d0971145f2
3 changed files with 338 additions and 0 deletions
199
Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java
Normal file
199
Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
package mage.cards.u;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageObjectReference;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.EntersBattlefieldAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.dynamicvalue.common.CountersSourceCount;
|
||||||
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.CastSourceTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.abilities.hint.ValueHint;
|
||||||
|
import mage.abilities.keyword.AnnihilatorAbility;
|
||||||
|
import mage.abilities.keyword.WardAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetOpponent;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Susucr
|
||||||
|
*/
|
||||||
|
public final class UlamogTheDefiler extends CardImpl {
|
||||||
|
|
||||||
|
private static final DynamicValue xValue = new CountersSourceCount(CounterType.P1P1);
|
||||||
|
|
||||||
|
public UlamogTheDefiler(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{10}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.ELDRAZI);
|
||||||
|
this.power = new MageInt(7);
|
||||||
|
this.toughness = new MageInt(7);
|
||||||
|
|
||||||
|
// When you cast this spell, target opponent exiles half their library, rounded up.
|
||||||
|
Ability ability = new CastSourceTriggeredAbility(new UlamogTheDefilerTargetEffect());
|
||||||
|
ability.addTarget(new TargetOpponent());
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Ward--Sacrifice two permanents.
|
||||||
|
this.addAbility(new WardAbility(new SacrificeTargetCost(2, StaticFilters.FILTER_PERMANENT)));
|
||||||
|
|
||||||
|
// Ulamog, the Defiler enters the battlefield with a number of +1/+1 counters on it equal to the greatest mana value among cards in exile.
|
||||||
|
this.addAbility(
|
||||||
|
new EntersBattlefieldAbility(
|
||||||
|
new AddCountersSourceEffect(
|
||||||
|
CounterType.P1P1.createInstance(), UlamogTheDefilerValue.instance, false
|
||||||
|
), "with a number of +1/+1 counters on it equal to " +
|
||||||
|
"the greatest mana value among cards in exile"
|
||||||
|
).addHint(UlamogTheDefilerValue.hint)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ulamog has annihilator X, where X is the number of +1/+1 counters on it.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new UlamogTheDefilerContinuousAbility()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UlamogTheDefiler(final UlamogTheDefiler card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UlamogTheDefiler copy() {
|
||||||
|
return new UlamogTheDefiler(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UlamogTheDefilerTargetEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
UlamogTheDefilerTargetEffect() {
|
||||||
|
super(Outcome.Detriment);
|
||||||
|
staticText = "target opponent exiles half their library, rounded up";
|
||||||
|
}
|
||||||
|
|
||||||
|
private UlamogTheDefilerTargetEffect(final UlamogTheDefilerTargetEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UlamogTheDefilerTargetEffect copy() {
|
||||||
|
return new UlamogTheDefilerTargetEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player opponent = game.getPlayer(source.getFirstTarget());
|
||||||
|
if (opponent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int toExile = (opponent.getLibrary().size() + 1) / 2;
|
||||||
|
Set<Card> cards = opponent.getLibrary().getTopCards(game, toExile);
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
opponent.moveCardsToExile(cards, source, game, true, null, "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UlamogTheDefilerValue implements DynamicValue {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
static final Hint hint = new ValueHint("Greatest mana value among cards in exile", instance);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
|
return game.getExile()
|
||||||
|
.getAllCardsByRange(game, sourceAbility.getControllerId())
|
||||||
|
.stream()
|
||||||
|
.mapToInt(Card::getManaValue)
|
||||||
|
.max()
|
||||||
|
.orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicValue copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UlamogTheDefilerContinuousAbility extends ContinuousEffectImpl {
|
||||||
|
|
||||||
|
// Keep the last annihilator ability added.
|
||||||
|
private Ability ability;
|
||||||
|
// Keep the last annihilator amount added.
|
||||||
|
private int lastAmount;
|
||||||
|
|
||||||
|
UlamogTheDefilerContinuousAbility() {
|
||||||
|
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||||
|
staticText = "{this} has annihilator X, where X is the number of +1/+1 counters on it";
|
||||||
|
this.addDependencyType(DependencyType.AddingAbility);
|
||||||
|
this.ability = new AnnihilatorAbility(0);
|
||||||
|
this.lastAmount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UlamogTheDefilerContinuousAbility(final UlamogTheDefilerContinuousAbility effect) {
|
||||||
|
super(effect);
|
||||||
|
this.ability = effect.ability.copy();
|
||||||
|
// From GainAbilitySourceEffect:
|
||||||
|
ability.newId(); // This is needed if the effect is copied e.g. by a clone so the ability can be added multiple times to permanents
|
||||||
|
this.lastAmount = effect.lastAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Ability source, Game game) {
|
||||||
|
super.init(source, game);
|
||||||
|
if (getAffectedObjectsSet()) {
|
||||||
|
Permanent permanent = game.getPermanentEntering(source.getSourceId());
|
||||||
|
if (permanent != null) {
|
||||||
|
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1, game));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UlamogTheDefilerContinuousAbility copy() {
|
||||||
|
return new UlamogTheDefilerContinuousAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Permanent permanent;
|
||||||
|
if (getAffectedObjectsSet()) {
|
||||||
|
permanent = affectedObjectList.get(0).getPermanent(game);
|
||||||
|
} else {
|
||||||
|
permanent = game.getPermanent(source.getSourceId());
|
||||||
|
}
|
||||||
|
if (permanent != null) {
|
||||||
|
int amount = permanent.getCounters(game).getCount(CounterType.P1P1);
|
||||||
|
if (amount != lastAmount) {
|
||||||
|
// Only instantiate a new ability if the number of P1P1 counters changed.
|
||||||
|
ability = new AnnihilatorAbility(amount);
|
||||||
|
lastAmount = amount;
|
||||||
|
}
|
||||||
|
permanent.addAbility(ability, source.getSourceId(), game);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -146,6 +146,7 @@ public final class ModernHorizons3 extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Trickster's Elk", 175, Rarity.UNCOMMON, mage.cards.t.TrickstersElk.class));
|
cards.add(new SetCardInfo("Trickster's Elk", 175, Rarity.UNCOMMON, mage.cards.t.TrickstersElk.class));
|
||||||
cards.add(new SetCardInfo("Tune the Narrative", 75, Rarity.COMMON, mage.cards.t.TuneTheNarrative.class));
|
cards.add(new SetCardInfo("Tune the Narrative", 75, Rarity.COMMON, mage.cards.t.TuneTheNarrative.class));
|
||||||
cards.add(new SetCardInfo("Ugin's Labyrinth", 233, Rarity.MYTHIC, mage.cards.u.UginsLabyrinth.class));
|
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("Urza's Cave", 234, Rarity.UNCOMMON, mage.cards.u.UrzasCave.class));
|
cards.add(new SetCardInfo("Urza's Cave", 234, Rarity.UNCOMMON, mage.cards.u.UrzasCave.class));
|
||||||
cards.add(new SetCardInfo("Victimize", 278, Rarity.UNCOMMON, mage.cards.v.Victimize.class));
|
cards.add(new SetCardInfo("Victimize", 278, Rarity.UNCOMMON, mage.cards.v.Victimize.class));
|
||||||
cards.add(new SetCardInfo("Warren Soultrader", 110, Rarity.RARE, mage.cards.w.WarrenSoultrader.class));
|
cards.add(new SetCardInfo("Warren Soultrader", 110, Rarity.RARE, mage.cards.w.WarrenSoultrader.class));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
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 UlamogTheDefilerTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link mage.cards.u.UlamogTheDefiler Ulamog, the Defiler} {10}
|
||||||
|
* Legendary Creature — Eldrazi
|
||||||
|
* When you cast this spell, target opponent exiles half their library, rounded up.
|
||||||
|
* Ward—Sacrifice two permanents.
|
||||||
|
* Ulamog, the Defiler enters the battlefield with a number of +1/+1 counters on it equal to the greatest mana value among cards in exile.
|
||||||
|
* Ulamog has annihilator X, where X is the number of +1/+1 counters on it.
|
||||||
|
* 7/7
|
||||||
|
*/
|
||||||
|
private static final String ulamog = "Ulamog, the Defiler";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnlyLandsExiled() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
skipInitShuffling();
|
||||||
|
removeAllCardsFromLibrary(playerB);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, ulamog);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 10);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 10);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Taiga", 11);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ulamog);
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
|
||||||
|
attack(3, playerA, ulamog, playerB);
|
||||||
|
// Annihilator 0 triggers
|
||||||
|
|
||||||
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerB, 6);
|
||||||
|
assertLife(playerB, 20 - 7);
|
||||||
|
assertGraveyardCount(playerB, "Mountain", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Annihilator2_FromFreshlyExiled() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
skipInitShuffling();
|
||||||
|
removeAllCardsFromLibrary(playerB);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, ulamog);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 10);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 10);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Taiga", 11);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Grizzly Bears");
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Elite Vanguard");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ulamog);
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
|
||||||
|
attack(3, playerA, ulamog, playerB);
|
||||||
|
// Annihilator 2 triggers
|
||||||
|
setChoice(playerB, "Mountain", 2);
|
||||||
|
|
||||||
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerB, 7);
|
||||||
|
assertExileCount(playerB, "Elite Vanguard", 1);
|
||||||
|
assertExileCount(playerB, "Grizzly Bears", 1);
|
||||||
|
assertLife(playerB, 20 - 9);
|
||||||
|
assertGraveyardCount(playerB, "Mountain", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Annihilator2_FromOldExiled() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
skipInitShuffling();
|
||||||
|
removeAllCardsFromLibrary(playerB);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, ulamog);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 10);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 10);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Taiga", 11);
|
||||||
|
addCard(Zone.EXILED, playerA, "Grizzly Bears");
|
||||||
|
addCard(Zone.EXILED, playerA, "Elite Vanguard");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ulamog);
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
|
||||||
|
attack(3, playerA, ulamog, playerB);
|
||||||
|
// Annihilator 2 triggers
|
||||||
|
setChoice(playerB, "Mountain", 2);
|
||||||
|
|
||||||
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerB, 6);
|
||||||
|
assertLife(playerB, 20 - 9);
|
||||||
|
assertGraveyardCount(playerB, "Mountain", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Annihilator4_Change_FromCounterLater() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
skipInitShuffling();
|
||||||
|
removeAllCardsFromLibrary(playerB);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, ulamog);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Luminarch Aspirant"); // At the beginning of combat on your turn, put a +1/+1 counter on target creature you control.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 10);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 10);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Taiga", 11);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Grizzly Bears");
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Elite Vanguard");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ulamog);
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, ulamog); // Aspirant trigger
|
||||||
|
|
||||||
|
attack(3, playerA, ulamog, playerB);
|
||||||
|
addTarget(playerA, ulamog); // Aspirant trigger
|
||||||
|
// Annihilator 4 triggers
|
||||||
|
setChoice(playerB, "Mountain", 4);
|
||||||
|
|
||||||
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerB, 7);
|
||||||
|
assertLife(playerB, 20 - 11);
|
||||||
|
assertGraveyardCount(playerB, "Mountain", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue