mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
[40K] Implement The Ruinous Powers; fix Fires of Mount Doom (#12722)
* Add starting point: have exile top w/ any color cast, right duration * Use random opponent * Add delayed triggered ability * rename MageIdentifier to AlternateCast * Fix FiresOfMountDoomDelayedTriggeredAbility * Add test for Fires of Mount Doom * Add test for The Ruinous Powers * Null checks
This commit is contained in:
parent
9fe5d6bd1b
commit
060551ab48
6 changed files with 252 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.MageIdentifier;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
|
|
@ -50,7 +51,8 @@ public final class FiresOfMountDoom extends CardImpl {
|
|||
this.addAbility(new SimpleActivatedAbility(
|
||||
Zone.BATTLEFIELD,
|
||||
new FiresOfMountDoomEffect(),
|
||||
new ManaCostsImpl<>("{2}{R}")));
|
||||
new ManaCostsImpl<>("{2}{R}")
|
||||
).setIdentifier(MageIdentifier.FiresOfMountDoomAlternateCast));
|
||||
}
|
||||
|
||||
private FiresOfMountDoom(final FiresOfMountDoom card) {
|
||||
|
|
@ -68,7 +70,7 @@ class FiresOfMountDoomEffect extends OneShotEffect {
|
|||
FiresOfMountDoomEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "exile the top card of your library. You may play that card this turn. " +
|
||||
"When you play a card this way, Fires of Mount Doom deals 2 damage to each player";
|
||||
"When you play a card this way, {this} deals 2 damage to each player";
|
||||
}
|
||||
|
||||
private FiresOfMountDoomEffect(final FiresOfMountDoomEffect effect) {
|
||||
|
|
@ -100,19 +102,9 @@ class FiresOfMountDoomEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: this is not quite right in corner cases.
|
||||
// Inspired by Havengul Lich which I feel has similar problems.
|
||||
// For instance what if the card is [[Squee, the Immortal]] and
|
||||
// is cast with squee AsThought.
|
||||
// Or if the card is played with the AsThought, then replayed
|
||||
// during the same turn. With current code, that would trigger
|
||||
// incorrectly again.
|
||||
// Is mor the solution there? Or having a way to get the specific
|
||||
// used AsThought and having a way to identify that it was the
|
||||
// same one as the FiresOfMountDoomEffect makeCardPlayable.
|
||||
class FiresOfMountDoomDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
||||
|
||||
private UUID cardId;
|
||||
private final UUID cardId;
|
||||
|
||||
public FiresOfMountDoomDelayedTriggeredAbility(UUID cardId) {
|
||||
super(new DamagePlayersEffect(2, TargetController.ANY), Duration.EndOfTurn);
|
||||
|
|
@ -133,6 +125,9 @@ class FiresOfMountDoomDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!event.hasApprovingIdentifier(MageIdentifier.FiresOfMountDoomAlternateCast)){
|
||||
return false;
|
||||
}
|
||||
return event.getSourceId().equals(cardId);
|
||||
}
|
||||
|
||||
|
|
|
|||
138
Mage.Sets/src/mage/cards/t/TheRuinousPowers.java
Normal file
138
Mage.Sets/src/mage/cards/t/TheRuinousPowers.java
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import mage.MageIdentifier;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jimga150
|
||||
*/
|
||||
public final class TheRuinousPowers extends CardImpl {
|
||||
|
||||
public TheRuinousPowers(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{R}");
|
||||
|
||||
|
||||
// At the beginning of your upkeep, choose an opponent at random. Exile the top card of that player's library.
|
||||
// Until end of turn, you may play that card and you may spend mana as though it were mana of any color to cast it.
|
||||
// When you cast a spell this way, its owner loses life equal to its mana value.
|
||||
Ability ability = new BeginningOfUpkeepTriggeredAbility(new TheRuinousPowersEffect(), TargetController.YOU, false)
|
||||
.setIdentifier(MageIdentifier.TheRuinousPowersAlternateCast);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private TheRuinousPowers(final TheRuinousPowers card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRuinousPowers copy() {
|
||||
return new TheRuinousPowers(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Based on YouFindSomePrisonersEffect
|
||||
class TheRuinousPowersEffect extends OneShotEffect {
|
||||
|
||||
TheRuinousPowersEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "choose an opponent at random. Exile the top card of that player's library. Until end of turn, " +
|
||||
"you may play that card and you may spend mana as though it were mana of any color to cast it. " +
|
||||
"When you cast a spell this way, its owner loses life equal to its mana value.";
|
||||
}
|
||||
|
||||
private TheRuinousPowersEffect(final TheRuinousPowersEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRuinousPowersEffect copy() {
|
||||
return new TheRuinousPowersEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null){
|
||||
return false;
|
||||
}
|
||||
List<UUID> opponents = new ArrayList<>(game.getOpponents(source.getControllerId()));
|
||||
if (opponents.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Player opponent = game.getPlayer(opponents.get(RandomUtil.nextInt(opponents.size())));
|
||||
if (opponent == null) {
|
||||
return false;
|
||||
}
|
||||
Card card = opponent.getLibrary().getFromTop(game);
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
if (card != null) {
|
||||
CardUtil.makeCardPlayable(game, source, card, false, Duration.EndOfTurn, true);
|
||||
game.addDelayedTriggeredAbility(new TheRuinousPowersTriggeredAbility(card.getId()), source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Based on FiresOfMountDoomDelayedTriggeredAbility
|
||||
class TheRuinousPowersTriggeredAbility extends DelayedTriggeredAbility {
|
||||
|
||||
private final UUID cardId;
|
||||
|
||||
public TheRuinousPowersTriggeredAbility(UUID cardId) {
|
||||
super(null, Duration.EndOfTurn);
|
||||
this.cardId = cardId;
|
||||
}
|
||||
|
||||
private TheRuinousPowersTriggeredAbility(final TheRuinousPowersTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.cardId = ability.cardId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.SPELL_CAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!event.getSourceId().equals(cardId)){
|
||||
return false;
|
||||
}
|
||||
if (!event.hasApprovingIdentifier(MageIdentifier.TheRuinousPowersAlternateCast)){
|
||||
return false;
|
||||
}
|
||||
Card card = game.getCard(cardId);
|
||||
if (card == null){
|
||||
return false;
|
||||
}
|
||||
Player owner = game.getPlayer(card.getOwnerId());
|
||||
if (owner == null){
|
||||
return false;
|
||||
}
|
||||
owner.loseLife(card.getManaValue(), game, this, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRuinousPowersTriggeredAbility copy() {
|
||||
return new TheRuinousPowersTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When you cast a spell this way, its owner loses life equal to its mana value.";
|
||||
}
|
||||
}
|
||||
|
|
@ -265,6 +265,7 @@ public final class Warhammer40000 extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("The Golden Throne", 157, Rarity.RARE, mage.cards.t.TheGoldenThrone.class));
|
||||
cards.add(new SetCardInfo("The Horus Heresy", 126, Rarity.RARE, mage.cards.t.TheHorusHeresy.class));
|
||||
cards.add(new SetCardInfo("The Lost and the Damned", 129, Rarity.UNCOMMON, mage.cards.t.TheLostAndTheDamned.class));
|
||||
cards.add(new SetCardInfo("The Ruinous Powers", 139, Rarity.RARE, mage.cards.t.TheRuinousPowers.class));
|
||||
cards.add(new SetCardInfo("The Swarmlord", 4, Rarity.MYTHIC, mage.cards.t.TheSwarmlord.class));
|
||||
cards.add(new SetCardInfo("The War in Heaven", 69, Rarity.RARE, mage.cards.t.TheWarInHeaven.class));
|
||||
cards.add(new SetCardInfo("Their Name Is Death", 62, Rarity.RARE, mage.cards.t.TheirNameIsDeath.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @author jimga150
|
||||
*/
|
||||
public class FiresOfMountDoomTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.f.FiresOfMountDoom Fires of Mount Doom}
|
||||
* Legendary Enchantment
|
||||
* When Fires of Mount Doom enters the battlefield, it deals 2 damage to target creature an opponent controls.
|
||||
* Destroy all Equipment attached to that creature.
|
||||
* {2}{R}: Exile the top card of your library. You may play that card this turn.
|
||||
* When you play a card this way, Fires of Mount Doom deals 2 damage to each player.
|
||||
*/
|
||||
private static final String fires = "Fires of Mount Doom";
|
||||
|
||||
@Test
|
||||
public void test_castWithDamage() {
|
||||
// Test the damaging triggered ability
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fires, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||
addCard(Zone.LIBRARY, playerA, "Squee, the Immortal", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Squee, the Immortal");
|
||||
setChoice(playerA, fires); // Choose Fires as the approving object
|
||||
|
||||
skipInitShuffling();
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Squee, the Immortal", 1);
|
||||
assertLife(playerB, currentGame.getStartingLife() - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_castNoDamage() {
|
||||
// Test that the damaging triggered ability doesnt trigger if something else allows the exiled card to be cast
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fires, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||
addCard(Zone.LIBRARY, playerA, "Squee, the Immortal", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Squee, the Immortal");
|
||||
setChoice(playerA, "Squee"); // Choose Squee as the approving object (which casts it normally)
|
||||
|
||||
skipInitShuffling();
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Squee, the Immortal", 1);
|
||||
assertLife(playerB, currentGame.getStartingLife());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @author jimga150
|
||||
*/
|
||||
public class TheRuinousPowersTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.t.TheRuinousPowers The Ruinous Powers}
|
||||
* Enchantment
|
||||
* At the beginning of your upkeep, choose an opponent at random. Exile the top card of that player’s library.
|
||||
* Until end of turn, you may play that card and you may spend mana as though it were mana of any color to cast it.
|
||||
* When you cast a spell this way, its owner loses life equal to its mana value.
|
||||
*/
|
||||
private static final String powers = "The Ruinous Powers";
|
||||
|
||||
@Test
|
||||
public void test_castWithDamage() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, powers, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||
addCard(Zone.LIBRARY, playerB, "Squee, the Immortal", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Squee, the Immortal");
|
||||
|
||||
skipInitShuffling();
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Squee, the Immortal", 1);
|
||||
assertLife(playerB, currentGame.getStartingLife() - 3);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,8 @@ public enum MageIdentifier {
|
|||
WithoutPayingManaCostAlternateCast,
|
||||
AlurenAlternateCast,
|
||||
OfferingAlternateCast,
|
||||
TheRuinousPowersAlternateCast,
|
||||
FiresOfMountDoomAlternateCast,
|
||||
PrimalPrayersAlternateCast;
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue