diff --git a/Mage.Sets/src/mage/cards/m/MegatronDestructiveForce.java b/Mage.Sets/src/mage/cards/m/MegatronDestructiveForce.java
new file mode 100644
index 00000000000..fb0cc217da0
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MegatronDestructiveForce.java
@@ -0,0 +1,161 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.TransformSourceEffect;
+import mage.abilities.hint.StaticHint;
+import mage.abilities.keyword.LivingMetalAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author Susucr
+ */
+public final class MegatronDestructiveForce extends CardImpl {
+ public MegatronDestructiveForce(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "");
+
+ this.supertype.add(SuperType.LEGENDARY);
+ this.subtype.add(SubType.VEHICLE);
+ this.power = new MageInt(4);
+ this.toughness = new MageInt(5);
+ this.color.setRed(true);
+ this.color.setWhite(true);
+ this.color.setBlack(true);
+ this.nightCard = true;
+
+ // Living metal
+ this.addAbility(new LivingMetalAbility());
+
+ // Whenever Megatron attacks, you may sacrifice another artifact. When you do, Megatron deals damage equal to the sacrificed artifact's mana value to target creature. If excess damage would be dealt to that creature this way, instead that damage is dealt to that creature's controller and you convert Megatron.
+ this.addAbility(new AttacksTriggeredAbility(new MegatronDestructiveForceEffect()));
+ }
+
+ private MegatronDestructiveForce(final MegatronDestructiveForce card) {
+ super(card);
+ }
+
+ @Override
+ public MegatronDestructiveForce copy() {
+ return new MegatronDestructiveForce(this);
+ }
+}
+
+class MegatronDestructiveForceEffect extends OneShotEffect {
+
+ MegatronDestructiveForceEffect() {
+ super(Outcome.Benefit);
+ staticText = "you may sacrifice another artifact. When you do, {this} deals damage equal to the sacrificed artifact's mana value to target creature. If excess damage would be dealt to that creature this way, instead that damage is dealt to that creature's controller and you convert {this}.";
+ }
+
+ private MegatronDestructiveForceEffect(final MegatronDestructiveForceEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public MegatronDestructiveForceEffect copy() {
+ return new MegatronDestructiveForceEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player player = game.getPlayer(source.getControllerId());
+ if (player == null) {
+ return false;
+ }
+ TargetPermanent target = new TargetPermanent(
+ 0, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_ARTIFACT_SHORT_TEXT, true
+ );
+ player.choose(outcome, target, source, game);
+ Permanent permanent = game.getPermanent(target.getFirstTarget());
+ if (permanent == null) {
+ return false;
+ }
+
+ int manaValue = Math.max(permanent.getManaValue(), 0);
+ if (!permanent.sacrifice(source, game)) {
+ return false;
+ }
+
+ ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+ new MegatronDestructiveForceReflexiveEffect(manaValue), false
+ );
+ ability.addHint(new StaticHint("Sacrificed artifact mana value: " + manaValue));
+ ability.addTarget(new TargetCreaturePermanent());
+ game.fireReflexiveTriggeredAbility(ability, source);
+ return true;
+ }
+}
+
+class MegatronDestructiveForceReflexiveEffect extends OneShotEffect {
+
+ private final int value;
+
+ MegatronDestructiveForceReflexiveEffect(int value) {
+ super(Outcome.Damage);
+ staticText = "{this} deals damage equal to the sacrificed artifact's mana value to target " +
+ "creature. If excess damage would be dealt to that creature this way, instead that damage " +
+ "is dealt to that creature's controller and you convert {this}.";
+ this.value = value;
+ }
+
+ private MegatronDestructiveForceReflexiveEffect(final MegatronDestructiveForceReflexiveEffect effect) {
+ super(effect);
+ this.value = effect.value;
+ }
+
+ @Override
+ public MegatronDestructiveForceReflexiveEffect copy() {
+ return new MegatronDestructiveForceReflexiveEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Permanent sourcePermanent = source.getSourcePermanentOrLKI(game);
+ if (sourcePermanent == null) {
+ return false;
+ }
+
+ if (value < 1) {
+ return false;
+ }
+
+ Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+ if (permanent == null) {
+ return false;
+ }
+
+ int lethal = permanent.getLethalDamage(source.getSourceId(), game);
+ int excess = value - lethal;
+ if (excess <= 0) {
+ // no excess damage.
+ permanent.damage(value, source.getSourceId(), source, game);
+ return true;
+ }
+
+ // excess damage. dealing excess to controller's instead. And convert Megatron.
+ permanent.damage(lethal, source.getSourceId(), source, game);
+ Player player = game.getPlayer(permanent.getControllerId());
+ if (player != null) {
+ player.damage(excess, source, game);
+ }
+ new TransformSourceEffect().apply(game, source);
+
+ return true;
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/m/MegatronTyrant.java b/Mage.Sets/src/mage/cards/m/MegatronTyrant.java
new file mode 100644
index 00000000000..acbd1c0d3ce
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MegatronTyrant.java
@@ -0,0 +1,111 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.Mana;
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbility;
+import mage.abilities.common.BeginningOfPostCombatMainTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.common.OpponentsLostLifeCount;
+import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
+import mage.abilities.effects.common.TransformSourceEffect;
+import mage.abilities.effects.mana.DynamicManaEffect;
+import mage.abilities.keyword.MoreThanMeetsTheEyeAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+
+import java.util.UUID;
+
+/**
+ * @author Susucr
+ */
+public final class MegatronTyrant extends CardImpl {
+
+ public MegatronTyrant(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}{B}");
+
+ this.supertype.add(SuperType.LEGENDARY);
+ this.subtype.add(SubType.ROBOT);
+ this.power = new MageInt(7);
+ this.toughness = new MageInt(5);
+ this.secondSideCardClazz = mage.cards.m.MegatronDestructiveForce.class;
+
+ // More Than Meets the Eye {1}{R}{W}{B}
+ this.addAbility(new MoreThanMeetsTheEyeAbility(this, "{1}{R}{W}{B}"));
+
+ // Your opponents can't cast spells during combat.
+ this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MegatronTyrantCantCastSpellsEffect()));
+
+ // At the beginning of your postcombat main phase, you may convert Megatron. If you do, add {C} for each 1 life your opponents have lost this turn.
+ TriggeredAbility trigger = new BeginningOfPostCombatMainTriggeredAbility(
+ new TransformSourceEffect().setText("convert {this}"),
+ TargetController.YOU,
+ true
+ );
+ trigger.addEffect(
+ new DynamicManaEffect(
+ Mana.ColorlessMana(1),
+ OpponentsLostLifeCount.instance,
+ "add {C} for each 1 life your opponents have lost this turn"
+ ).concatBy("If you do, ")
+ );
+
+ this.addAbility(trigger);
+ }
+
+ private MegatronTyrant(final MegatronTyrant card) {
+ super(card);
+ }
+
+ @Override
+ public MegatronTyrant copy() {
+ return new MegatronTyrant(this);
+ }
+}
+
+class MegatronTyrantCantCastSpellsEffect extends ContinuousRuleModifyingEffectImpl {
+
+ MegatronTyrantCantCastSpellsEffect() {
+ super(Duration.WhileOnBattlefield, Outcome.Benefit);
+ staticText = "Your opponents can't cast spells during combat.";
+ }
+
+ private MegatronTyrantCantCastSpellsEffect(final MegatronTyrantCantCastSpellsEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public MegatronTyrantCantCastSpellsEffect copy() {
+ return new MegatronTyrantCantCastSpellsEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ return true;
+ }
+
+ @Override
+ public String getInfoMessage(Ability source, GameEvent event, Game game) {
+ MageObject mageObject = game.getObject(source);
+ if (mageObject != null) {
+ return "You can't cast spells during combat (" + mageObject.getIdName() + ").";
+ }
+ return null;
+ }
+
+ @Override
+ public boolean checksEventType(GameEvent event, Game game) {
+ return event.getType() == GameEvent.EventType.CAST_SPELL;
+ }
+
+ @Override
+ public boolean applies(GameEvent event, Ability source, Game game) {
+ return game.getOpponents(source.getControllerId()).contains(event.getPlayerId())
+ && game.getTurnPhaseType() == TurnPhase.COMBAT;
+ }
+
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/Transformers.java b/Mage.Sets/src/mage/sets/Transformers.java
index 70860300557..e4323473389 100644
--- a/Mage.Sets/src/mage/sets/Transformers.java
+++ b/Mage.Sets/src/mage/sets/Transformers.java
@@ -29,6 +29,8 @@ public final class Transformers extends ExpansionSet {
cards.add(new SetCardInfo("Goldbug, Scrappy Scout", 11, Rarity.MYTHIC, mage.cards.g.GoldbugScrappyScout.class));
cards.add(new SetCardInfo("Jetfire, Air Guardian", 3, Rarity.MYTHIC, mage.cards.j.JetfireAirGuardian.class));
cards.add(new SetCardInfo("Jetfire, Ingenious Scientist", 3, Rarity.MYTHIC, mage.cards.j.JetfireIngeniousScientist.class));
+ cards.add(new SetCardInfo("Megatron, Destructive Force", 12, Rarity.MYTHIC, mage.cards.m.MegatronDestructiveForce.class));
+ cards.add(new SetCardInfo("Megatron, Tyrant", 12, Rarity.MYTHIC, mage.cards.m.MegatronTyrant.class));
cards.add(new SetCardInfo("Ratchet, Field Medic", 2, Rarity.MYTHIC, mage.cards.r.RatchetFieldMedic.class));
cards.add(new SetCardInfo("Ratchet, Rescue Racer", 2, Rarity.MYTHIC, mage.cards.r.RatchetRescueRacer.class));
cards.add(new SetCardInfo("Ultra Magnus, Armored Carrier", 15, Rarity.MYTHIC, mage.cards.u.UltraMagnusArmoredCarrier.class));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java
index 6f415cc488b..33a041301ef 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java
@@ -31,12 +31,12 @@ public class DisturbTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
- checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true);
+ checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", true);
// cast with disturb
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2);
- activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb");
- checkStackObject("on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", 1);
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb");
+ checkStackObject("on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", 1);
runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
// Stack must contain another card side, so spell/card characteristics must be diff from main side (only mana value is same)
Spell spell = (Spell) game.getStack().getFirst();
@@ -92,10 +92,10 @@ public class DisturbTest extends CardTestPlayerBase {
addCustomEffect_SpellCostModification(playerA, -1);
- checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true);
+ checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", true);
// cast with disturb
- activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb");
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb");
runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
Spell spell = (Spell) game.getStack().getFirst();
Assert.assertEquals("mana value must be from main side", 2, spell.getManaValue());
@@ -121,7 +121,7 @@ public class DisturbTest extends CardTestPlayerBase {
//
addCustomEffect_SpellCostModification(playerA, 1);
- checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", false);
+ checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", false);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
@@ -145,11 +145,11 @@ public class DisturbTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
- checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true);
+ checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", true);
// cast with disturb
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2);
- activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb");
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb");
// prepare copy of spell
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Double Major", "Hook-Haunt Drifter", "Hook-Haunt Drifter");
checkStackSize("before copy spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
@@ -181,10 +181,10 @@ public class DisturbTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Counterspell", 1); // {U}{U}
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
- checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true);
+ checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", true);
// cast with disturb
- activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb");
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb");
// counter it
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Hook-Haunt Drifter", "Hook-Haunt Drifter");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
@@ -213,7 +213,7 @@ public class DisturbTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.HAND, playerA, lightningBolt);
- activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + ghastlyMimictry + " with Disturb");
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + ghastlyMimictry + " using Disturb");
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index c151996d6c6..1ae63289b97 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -434,6 +434,8 @@ public abstract class AbilityImpl implements Ability {
case FLASHBACK:
case MADNESS:
case TRANSFORMED:
+ case DISTURB:
+ case MORE_THAN_MEETS_THE_EYE:
// from Snapcaster Mage:
// If you cast a spell from a graveyard using its flashback ability, you can't pay other alternative costs
// (such as that of Foil). (2018-12-07)
diff --git a/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java b/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java
index f2b4d00575e..20ec3f5999e 100644
--- a/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java
@@ -34,10 +34,11 @@ public class DisturbAbility extends SpellAbility {
this.newId();
// getSecondFaceSpellAbility() already verified that second face exists
- this.setCardName(card.getSecondCardFace().getName() + " with Disturb");
+ this.setCardName(card.getSecondCardFace().getName());
+
this.zone = Zone.GRAVEYARD;
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
- this.spellAbilityCastMode = SpellAbilityCastMode.TRANSFORMED;
+ this.setSpellAbilityCastMode(SpellAbilityCastMode.DISTURB);
this.manaCost = manaCost;
this.getManaCosts().clear();
diff --git a/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java b/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java
index ab19446e1f6..f1c8a698546 100644
--- a/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java
@@ -22,9 +22,10 @@ public class MoreThanMeetsTheEyeAbility extends SpellAbility {
this.newId();
// getSecondFaceSpellAbility() already verified that second face exists
- this.setCardName(card.getSecondCardFace().getName() + " with Disturb");
+ this.setCardName(card.getSecondCardFace().getName());
+
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
- this.spellAbilityCastMode = SpellAbilityCastMode.TRANSFORMED;
+ this.setSpellAbilityCastMode(SpellAbilityCastMode.MORE_THAN_MEETS_THE_EYE);
this.manaCost = manaCost;
this.getManaCosts().clear();
@@ -63,7 +64,7 @@ public class MoreThanMeetsTheEyeAbility extends SpellAbility {
@Override
public String getRule() {
return "More Than Meets The Eye " + this.manaCost
- + " (You may cast this card converted for" + this.manaCost + ".)";
+ + " (You may cast this card converted for " + this.manaCost + ".)";
}
}
diff --git a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java
index 200c99fd91a..e073465fea0 100644
--- a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java
+++ b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java
@@ -5,7 +5,6 @@ import mage.cards.Card;
import mage.game.Game;
/**
- *
* @author LevelX2
*/
public enum SpellAbilityCastMode {
@@ -13,12 +12,26 @@ public enum SpellAbilityCastMode {
MADNESS("Madness"),
FLASHBACK("Flashback"),
BESTOW("Bestow"),
- TRANSFORMED("Transformed");
+ TRANSFORMED("Transformed", true),
+ DISTURB("Disturb", true),
+ MORE_THAN_MEETS_THE_EYE("More than Meets the Eye", true);
private final String text;
+ // Should the cast mode use the second face?
+ private final boolean isTransformed;
+
+ public boolean isTransformed() {
+ return this.isTransformed;
+ }
+
SpellAbilityCastMode(String text) {
+ this(text, false);
+ }
+
+ SpellAbilityCastMode(String text, boolean isTransformed) {
this.text = text;
+ this.isTransformed = isTransformed;
}
@Override
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index 0e7da518544..6d8314f40bf 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -75,7 +75,7 @@ public class Spell extends StackObjectImpl implements Card {
Card affectedCard = card;
// TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides)
- if (ability.getSpellAbilityCastMode() == SpellAbilityCastMode.TRANSFORMED && affectedCard.getSecondCardFace() != null) {
+ if (ability.getSpellAbilityCastMode().isTransformed() && affectedCard.getSecondCardFace() != null) {
// simulate another side as new card (another code part in continues effect from disturb ability)
affectedCard = TransformAbility.transformCardSpellStatic(card, card.getSecondCardFace(), game);
}