[OTC] Implement Smirking Spelljacker

This commit is contained in:
Susucre 2024-04-06 16:26:16 +02:00
parent 788fbd26e1
commit a86b629abe
4 changed files with 188 additions and 0 deletions

View file

@ -0,0 +1,129 @@
package mage.cards.s;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.MayCastTargetCardEffect;
import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.TargetSpell;
import mage.target.common.TargetCardInExile;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author Susucr
*/
public final class SmirkingSpelljacker extends CardImpl {
private static final FilterSpell filter = new FilterSpell("spell an opponent controls");
static {
filter.add(TargetController.OPPONENT.getControllerPredicate());
}
public SmirkingSpelljacker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}");
this.subtype.add(SubType.DJINN);
this.subtype.add(SubType.WIZARD);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Flash
this.addAbility(FlashAbility.getInstance());
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Smirking Spelljacker enters the battlefield, exile target spell an opponent controls.
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect().setToSourceExileZone(true));
ability.addTarget(new TargetSpell(filter));
this.addAbility(ability);
// Whenever Smirking Spelljacker attacks, if a card is exiled with it, you may cast the exiled card without paying its mana cost.
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new AttacksTriggeredAbility(new SmirkingSpelljackerEffect()),
SmirkingSpelljackerCondition.instance,
"Whenever Smirking Spelljacker attacks, if a card is exiled with it, "
+ "you may cast the exiled card without paying its mana cost."
));
}
private SmirkingSpelljacker(final SmirkingSpelljacker card) {
super(card);
}
@Override
public SmirkingSpelljacker copy() {
return new SmirkingSpelljacker(this);
}
}
enum SmirkingSpelljackerCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
MageObject sourceObject = source.getSourceObject(game);
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceObject.getId(), sourceObject.getZoneChangeCounter(game)));
return exile != null && !exile.isEmpty();
}
@Override
public String toString() {
return "if a card is exiled with it";
}
}
class SmirkingSpelljackerEffect extends OneShotEffect {
SmirkingSpelljackerEffect() {
super(Outcome.PlayForFree);
staticText = "you may cast the exiled card without paying its mana cost";
}
private SmirkingSpelljackerEffect(final SmirkingSpelljackerEffect effect) {
super(effect);
}
@Override
public SmirkingSpelljackerEffect copy() {
return new SmirkingSpelljackerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
FilterCard filter = new FilterCard("card exiled with " + CardUtil.getSourceLogName(game, source));
TargetCard target = new TargetCardInExile(1, 1, filter, CardUtil.getExileZoneId(game, source));
target.withNotTarget(true);
controller.choose(Outcome.PlayForFree, target, source, game);
new MayCastTargetCardEffect(CastManaAdjustment.WITHOUT_PAYING_MANA_COST)
.setTargetPointer(new FixedTarget(target.getFirstTarget()))
.apply(game, source);
return true;
}
}

View file

@ -237,6 +237,7 @@ public final class OutlawsOfThunderJunctionCommander extends ExpansionSet {
cards.add(new SetCardInfo("Siphon Insight", 242, Rarity.RARE, mage.cards.s.SiphonInsight.class));
cards.add(new SetCardInfo("Skullwinder", 207, Rarity.UNCOMMON, mage.cards.s.Skullwinder.class));
cards.add(new SetCardInfo("Slither Blade", 114, Rarity.COMMON, mage.cards.s.SlitherBlade.class));
cards.add(new SetCardInfo("Smirking Spelljacker", 16, Rarity.RARE, mage.cards.s.SmirkingSpelljacker.class));
cards.add(new SetCardInfo("Smoldering Marsh", 321, Rarity.RARE, mage.cards.s.SmolderingMarsh.class));
cards.add(new SetCardInfo("Sol Ring", 267, Rarity.UNCOMMON, mage.cards.s.SolRing.class));
cards.add(new SetCardInfo("Springbloom Druid", 208, Rarity.COMMON, mage.cards.s.SpringbloomDruid.class));

View file

@ -0,0 +1,51 @@
package org.mage.test.cards.single.otc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class SmirkingSpelljackerTest extends CardTestPlayerBase {
/**
* {@link mage.cards.s.SmirkingSpelljacker Smirking Spelljacker} {4}{U}
* Creature Djinn Wizard Rogue
* Flash
* Flying
* When Smirking Spelljacker enters the battlefield, exile target spell an opponent controls.
* Whenever Smirking Spelljacker attacks, if a card is exiled with it, you may cast the exiled card without paying its mana cost.
* 3/3
*/
private static final String spelljacker = "Smirking Spelljacker";
@Test
public void test_Simple() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerB, spelljacker);
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, spelljacker);
addTarget(playerB, "Lightning Bolt");
checkExileCount("Bolt in exile", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", 1);
attack(2, playerB, spelljacker, playerA);
setChoice(playerB, "Lightning Bolt"); // choosing bolt
setChoice(playerB, true); // yes to cast bolt
addTarget(playerB, playerA); // target for bolt
setStopAt(2, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20 - 3 - 3); // 3 from bolt, 3 from spelljacker
assertPermanentCount(playerB, spelljacker, 1);
assertGraveyardCount(playerA, "Lightning Bolt", 1);
}
}

View file

@ -34,6 +34,13 @@ public class MayCastTargetCardEffect extends OneShotEffect {
this(CastManaAdjustment.NONE, thenExile);
}
/**
* Allows to cast the target card immediately, either for its cost or with a modifier (like for free, or mana as any type).
*/
public MayCastTargetCardEffect(CastManaAdjustment manaAdjustment) {
this(manaAdjustment, false);
}
/**
* Allows to cast the target card immediately, either for its cost or with a modifier (like for free, or mana as any type).
*/