Merge pull request 'master' (#19) from External/mage:master into master
All checks were successful
/ example-docker-compose (push) Successful in 15m4s

Reviewed-on: #19
This commit is contained in:
Failure 2025-03-02 17:39:59 -08:00
commit d1ca46fd85
111 changed files with 3335 additions and 976 deletions

View file

@ -152,7 +152,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
setBackgroundPainter(mwPanelPainter);
title = new ColorPane();
title.setFont(new Font("Times New Roman", 1, sizeMod(15)));
title.setFont(new Font("Times New Roman", Font.BOLD, sizeMod(15)));
title.setEditable(false);
title.setFocusCycleRoot(false);
title.setOpaque(false);
@ -186,11 +186,12 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
rows.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent evt) {
if (SwingUtilities.isLeftMouseButton(evt)) {
if (SwingUtilities.isLeftMouseButton(evt) && !rows.isSelectionEmpty()) {
objectMouseClicked(evt);
}
}
});
rows.setSelectedIndex(0);
rows.setFont(new Font("Times New Roman", 1, sizeMod(17)));
rows.setBorder(BorderFactory.createEmptyBorder());
@ -233,18 +234,16 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
int notches = e.getWheelRotation();
int index = rows.getSelectedIndex();
if (notches < 0) {
if (index > 0) {
rows.setSelectedIndex(index - 1);
rows.repaint();
}
} else if (index < choices.size() - 1) {
rows.setSelectedIndex(index + 1);
rows.repaint();
int direction = e.getWheelRotation() < 0 ? -1 : +1;
int index = rows.getSelectedIndex() + direction;
if (index < 0) {
index = 0;
} else if (index >= choices.size()) {
index = choices.size() - 1;
}
rows.setSelectedIndex(index);
rows.repaint();
}
private void objectMouseClicked(MouseEvent event) {

View file

@ -81,7 +81,7 @@ public class DialogContainer extends JPanel {
case EMBLEMS: {
backgroundColor = new Color(0, 0, 50, 110);
alpha = 0;
ChoiceDialog dlg = new ChoiceDialog(params, "Command Zone (Commander, Emblems and Planes)");
ChoiceDialog dlg = new ChoiceDialog(params, "Command Zone (Commanders, Emblems, and Planes)");
add(dlg);
dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10);
dlg.updateSize(params.rect.width - 80, params.rect.height - 80);

View file

@ -687,11 +687,11 @@ public class PlayerPanelExt extends javax.swing.JPanel {
zonesPanel.setLayout(null);
zonesPanel.setOpaque(false);
// tools button like hints
// hints
toolHintsHelper = new JButton();
toolHintsHelper.setFont(this.getFont());
toolHintsHelper.setText("hints");
toolHintsHelper.setToolTipText("Open new card hints helper window");
toolHintsHelper.setText("Hints");
toolHintsHelper.setToolTipText("Open card hints helper window");
toolHintsHelper.addActionListener(this::btnToolHintsHelperActionPerformed);
toolHintsHelper.setBounds(sizeMod(3), sizeMod(2 + 21 + 2), sizeMod(73), sizeMod(21));
zonesPanel.add(toolHintsHelper);
@ -701,7 +701,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
image = ImageHelper.getImageFromResources("/info/command_zone.png");
resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
commandZone = new HoverButton(null, resized, resized, resized, r, this.guiScaleMod);
commandZone.setToolTipText("Command Zone (Commanders, Emblems and Planes)");
commandZone.setToolTipText("Command Zone (Commanders, Emblems, and Planes)");
commandZone.setOpaque(false);
commandZone.setObserver(() -> btnCommandZoneActionPerformed(null));
commandZone.setBounds(sizeMod(3), 0, sizeMod(21), sizeMod(21));

View file

@ -579,6 +579,10 @@ public class ScryfallImageSupportCards {
add("INR"); // Innistrad Remastered
add("DFT"); // Aetherdrift
add("DRC"); // Aetherdrift Commander
add("TDM"); // Tarkir: Dragonstorm
add("TDC"); // Tarkir: Dragonstorm Commander
add("FIN"); // Final Fantasy
add("FIC"); // Final Fantasy Commander
// Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks
add("CALC"); // Custom Alchemized versions of existing cards

View file

@ -2169,6 +2169,8 @@ public class ScryfallImageSupportTokens {
// WHO
put("WHO/Alien Insect", "https://api.scryfall.com/cards/twho/19/en?format=image");
put("WHO/Human Noble", "https://api.scryfall.com/cards/twho/7/en?format=image");
put("WHO/Horse", "https://api.scryfall.com/cards/twho/4/en?format=image");
// 8ED
put("8ED/Rukh", "https://api.scryfall.com/cards/p03/7/en?format=image");

View file

@ -64,10 +64,10 @@ public class MageTable extends JTable {
// html tooltip
java.awt.Point p = e.getPoint();
int colIndex = columnModel.getColumnIndexAtX(p.x);
TableColumn col = columnModel.getColumn(colIndex);
if (colIndex < 0) {
return "";
}
TableColumn col = columnModel.getColumn(colIndex);
int realIndex = col.getModelIndex();
String tip;

View file

@ -2951,7 +2951,7 @@ public class HumanPlayer extends PlayerImpl {
protected boolean passWithManaPoolCheck(Game game) {
if (userData.confirmEmptyManaPool()
&& game.getStack().isEmpty() && getManaPool().count() > 0 && getManaPool().canLostManaOnEmpty()) {
String message = GameLog.getPlayerConfirmColoredText("You still have mana in your mana pool and it will be lose. Pass anyway?");
String message = GameLog.getPlayerConfirmColoredText("You still have mana in your mana pool and it will be lost. Pass anyway?");
if (!chooseUse(Outcome.Detriment, message, null, game)) {
sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, game, null);
return false;

View file

@ -591,7 +591,7 @@ public class GameController implements GameCallback {
if (playerId != null) {
Player player = game.getPlayer(playerId);
if (player != null) {
game.informPlayers(player.getLogName() + " want to concede");
game.informPlayers(player.getLogName() + " wants to concede");
game.setConcedingPlayer(getPlayerId(userId));
}
}

View file

@ -0,0 +1,118 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CrewSaddleIncreasedPowerAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.EndTurnEffect;
import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.CrewAbility;
import mage.abilities.keyword.SaddleAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.SaddledSourceThisTurnPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author Jmlundeen
*/
public final class AlacrianArmory extends CardImpl {
private static final FilterCreaturePermanent creatureFilter = new FilterCreaturePermanent("Creatures you control");
private static final FilterPermanent mountOrVehicleFilter = new FilterPermanent("Mount or Vehicle you control");
static {
mountOrVehicleFilter.add(Predicates.or(
SubType.MOUNT.getPredicate(),
SubType.VEHICLE.getPredicate()
));
mountOrVehicleFilter.add(TargetController.YOU.getControllerPredicate());
}
public AlacrianArmory(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{W}");
// Creatures you control get +0/+1 and have vigilance.
Ability staticAbility = new SimpleStaticAbility(new BoostControlledEffect(0, 1, Duration.WhileOnBattlefield, creatureFilter));
Effect effect = new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, creatureFilter);
effect.setText("and have vigilance");
staticAbility.addEffect(effect);
this.addAbility(staticAbility);
// At the beginning of combat on your turn, choose up to one target Mount or Vehicle you control. Until end of turn, that permanent becomes saddled if it's a Mount and becomes an artifact creature if it's a Vehicle.
Ability animateAbility = new BeginningOfCombatTriggeredAbility(
new AlacrianArmoryAnimateEffect()
);
animateAbility.addTarget(new TargetPermanent(0, 1, mountOrVehicleFilter));
this.addAbility(animateAbility);
}
private AlacrianArmory(final AlacrianArmory card) {
super(card);
}
@Override
public AlacrianArmory copy() {
return new AlacrianArmory(this);
}
}
class AlacrianArmoryAnimateEffect extends OneShotEffect {
AlacrianArmoryAnimateEffect() {
super(Outcome.Benefit);
staticText = "choose up to one target Mount or Vehicle you control. " +
"Until end of turn, that permanent becomes saddled if it's a Mount " +
"and becomes an artifact creature if it's a Vehicle";
}
private AlacrianArmoryAnimateEffect(final AlacrianArmoryAnimateEffect effect) {
super(effect);
}
@Override
public AlacrianArmoryAnimateEffect copy() {
return new AlacrianArmoryAnimateEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent target = game.getPermanent(source.getFirstTarget());
if (target == null) {
return false;
}
if (target.hasSubtype(SubType.MOUNT, game)) {
target.getAbilities().stream().filter(
ability -> ability instanceof SaddleAbility)
.findFirst()
.ifPresent(ability -> game.fireEvent(GameEvent.getEvent(
GameEvent.EventType.MOUNT_SADDLED,
ability.getSourceId(),
ability, source.getControllerId()))
);
}
if (target.hasSubtype(SubType.VEHICLE, game)) {
game.addEffect(new AddCardTypeTargetEffect(Duration.EndOfTurn, CardType.CREATURE, CardType.ARTIFACT)
.setTargetPointer(new FixedTarget(target, game)), source);
}
return true;
}
}

View file

@ -0,0 +1,97 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksEachCombatStaticAbility;
import mage.abilities.effects.*;
import mage.abilities.effects.common.TargetPlayerGainControlSourceEffect;
import mage.abilities.effects.common.UntapSourceEffect;
import mage.abilities.effects.common.continuous.CantBeSacrificedSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.Objects;
import java.util.UUID;
public class AlexiosDeimosOfKosmos extends CardImpl {
public AlexiosDeimosOfKosmos(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.BERSERKER);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Trample
this.addAbility(TrampleAbility.getInstance());
// attacks each combat if able, can't be sacrificed, and can't attack it's owner
Ability ability = new AttacksEachCombatStaticAbility();
ability.addEffect(new CantBeSacrificedSourceEffect().setText(", can't be sacrificed"));
ability.addEffect(new AlexiosDeimosOfKosmosRestrictionEffect());
this.addAbility(ability);
// at the beginning of each player's upkeep, that player gains control of alexios, untaps it, and puts a +1/+1 counter on it. it gains haste until end of turn
Ability upkeepAbility = new BeginningOfUpkeepTriggeredAbility(TargetController.EACH_PLAYER, null, false);
upkeepAbility.addEffect(new TargetPlayerGainControlSourceEffect().setText("that player gains control of Alexios"));
upkeepAbility.addEffect(new UntapSourceEffect().setText(", untaps it"));
upkeepAbility.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)).setText(", and puts a +1/+1 counter on it"));
upkeepAbility.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("it gains haste until end of turn"));
this.addAbility(upkeepAbility);
}
public AlexiosDeimosOfKosmos(CardImpl card) {
super(card);
}
@Override
public AlexiosDeimosOfKosmos copy() {
return new AlexiosDeimosOfKosmos(this);
}
class AlexiosDeimosOfKosmosRestrictionEffect extends RestrictionEffect {
public AlexiosDeimosOfKosmosRestrictionEffect() {
super(Duration.WhileOnBattlefield);
this.staticText = ", and can't attack it's owner";
}
public AlexiosDeimosOfKosmosRestrictionEffect(RestrictionEffect effect) {
super(effect);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return Objects.equals(permanent.getId(), source.getSourceId());
}
@Override
public AlexiosDeimosOfKosmosRestrictionEffect copy() {
return new AlexiosDeimosOfKosmosRestrictionEffect(this);
}
@Override
public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) {
if (defenderId == null) {
return true;
}
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent == null) {
return true;
}
UUID ownerId = permanent.getOwnerId();
return !defenderId.equals(ownerId);
}
}
}

View file

@ -0,0 +1,60 @@
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.effects.common.ChooseACardNameEffect;
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.Game;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author jam736
*/
public final class AncientVendetta extends CardImpl {
public AncientVendetta(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}");
// Choose a card name. Search target opponents graveyard, hand, and library for up to four cards with that name and exile them. Then that player shuffles.
this.getSpellAbility().addEffect((new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL)));
this.getSpellAbility().addEffect(new AncientVendettaEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
private AncientVendetta(final AncientVendetta card) {
super(card);
}
@Override
public AncientVendetta copy() {
return new AncientVendetta(this);
}
}
class AncientVendettaEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
AncientVendettaEffect() {
super(true, "target opponent's", "up to four cards with that name", false, 4);
}
private AncientVendettaEffect(final AncientVendettaEffect effect) {
super(effect);
}
@Override
public AncientVendettaEffect copy() {
return new AncientVendettaEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
String chosenCardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
return applySearchAndExile(game, source, chosenCardName, getTargetPointer().getFirst(game, source));
}
}

View file

@ -0,0 +1,64 @@
package mage.cards.b;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.CreatureDiedControlledCondition;
import mage.abilities.condition.common.ModeChoiceSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ChooseModeEffect;
import mage.abilities.effects.common.SacrificeOpponentsEffect;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BarrensteppeSiege extends CardImpl {
private static final Condition condition1 = new ModeChoiceSourceCondition("Abzan");
private static final String rule1 = "&bull Abzan &mdash; At the beginning of your end step, " +
"put a +1/+1 counter on each creature you control.";
private static final Condition condition2 = new ModeChoiceSourceCondition("Mardu");
private static final String rule2 = "&bull Mardu &mdash; At the beginning of your end step, " +
"if a creature died under your control this turn, each opponent sacrifices a creature of their choice.";
public BarrensteppeSiege(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{B}");
// As this enchantment enters, choose Abzan or Mardu.
this.addAbility(new EntersBattlefieldAbility(
new ChooseModeEffect("Abzan or Mardu?", "Abzan", "Mardu"),
null, "As {this} enters, choose Abzan or Mardu.", ""
));
// * Abzan -- At the beginning of your end step, put a +1/+1 counter on each creature you control.
this.addAbility(new ConditionalTriggeredAbility(
new BeginningOfEndStepTriggeredAbility(new AddCountersAllEffect(
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE
)), condition1, rule1
));
// * Mardu -- At the beginning of your end step, if a creature died under your control this turn, each opponent sacrifices a creature of their choice.
this.addAbility(new ConditionalTriggeredAbility(
new BeginningOfEndStepTriggeredAbility(
new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_CREATURE)
).withInterveningIf(CreatureDiedControlledCondition.instance), condition2, rule2
));
}
private BarrensteppeSiege(final BarrensteppeSiege card) {
super(card);
}
@Override
public BarrensteppeSiege copy() {
return new BarrensteppeSiege(this);
}
}

View file

@ -61,7 +61,7 @@ public final class BaruWurmspeaker extends CardImpl {
).setText("and have trample"));
this.addAbility(ability);
// {7}{G}, {T}: Create a 4/4 green Wurm creature token. This ability costs {X} less to activate, whre X is the greatest power among Wurms you control.
// {7}{G}, {T}: Create a 4/4 green Wurm creature token. This ability costs {X} less to activate, where X is the greatest power among Wurms you control.
ability = new SimpleActivatedAbility(new CreateTokenEffect(new Wurm44Token()), new ManaCostsImpl<>("{7}{G}"));
ability.addCost(new TapSourceCost());
ability.addEffect(new InfoEffect("this ability costs {X} less to activate, " +

View file

@ -0,0 +1,124 @@
package mage.cards.b;
import mage.MageInt;
import mage.MageItem;
import mage.abilities.Ability;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Mode;
import mage.abilities.common.ActivateAbilityTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.CopyStackObjectEffect;
import mage.abilities.meta.OrTriggeredAbility;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.abilities.keyword.DoctorsCompanionAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.common.FilterInstantOrSorcerySpell;
import mage.filter.FilterSpell;
import mage.filter.FilterStackObject;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.stack.StackObject;
import mage.game.stack.Spell;
import mage.target.Target;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.UUID;
/**
*
* @author padfoot
*/
public final class BillPotts extends CardImpl {
private static final FilterSpell filterInstantOrSorcery = new FilterInstantOrSorcerySpell("an instant or sorcery that targets only {this}");
private static final FilterStackObject filterAbility = new FilterStackObject("an ability that targets only {this}");
static {
filterInstantOrSorcery.add(BillPottsPredicate.instance);
filterAbility.add(BillPottsPredicate.instance);
}
public BillPotts(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// Whenever you cast an instant or sorcery spell that targets only Bill Potts or activate an ability that targets only Bill Potts, copy that spell or ability. You may choose new targets for the copy. This ability triggers only once each turn.
this.addAbility(new OrTriggeredAbility(
Zone.BATTLEFIELD,
new CopyStackObjectEffect("that spell or ability"),
false,
"",
new SpellCastControllerTriggeredAbility(
null,
filterInstantOrSorcery,
false,
SetTargetPointer.SPELL
),
new ActivateAbilityTriggeredAbility(
null,
filterAbility,
SetTargetPointer.SPELL
)
).setTriggersLimitEachTurn(1));
// Doctor's companion
this.addAbility(DoctorsCompanionAbility.getInstance());
}
private BillPotts(final BillPotts card) {
super(card);
}
@Override
public BillPotts copy() {
return new BillPotts(this);
}
}
enum BillPottsPredicate implements ObjectSourcePlayerPredicate<StackObject> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<StackObject> input, Game game) {
List<UUID> oneTargetList = Arrays.asList(input.getSourceId());
return (makeStream(input, game).collect(Collectors.toList()).equals(oneTargetList));
}
private static final Stream<UUID> makeStream(ObjectSourcePlayer<StackObject> input, Game game) {
Abilities<Ability> objectAbilities = new AbilitiesImpl<>();
if (input.getObject() instanceof Spell) {
objectAbilities.addAll(((Spell) input.getObject()).getSpellAbilities());
} else {
objectAbilities.add(input.getObject().getStackAbility());
}
return objectAbilities
.stream()
.map(Ability::getModes)
.flatMap(m -> m.getSelectedModes().stream().map(m::get))
.filter(Objects::nonNull)
.map(Mode::getTargets)
.flatMap(Collection::stream)
.filter(t -> !t.isNotTarget())
.map(Target::getTargets)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.distinct();
}
}

View file

@ -1,26 +1,20 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
* @author mpouedras
* @author mpouedras, notgreat
*/
public final class BroodOfCockroaches extends CardImpl {
@ -33,7 +27,13 @@ public final class BroodOfCockroaches extends CardImpl {
// When Brood of Cockroaches is put into your graveyard from the battlefield,
// at the beginning of the next end step, you lose 1 life and return Brood of Cockroaches to your hand.
this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new BroodOfCockroachesEffect(), false, true));
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayed =
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new LoseLifeSourceControllerEffect(1));
delayed.addEffect(new ReturnSourceFromGraveyardToHandEffect().concatBy("and").setText("return this card to your hand"));
CreateDelayedTriggeredAbilityEffect delayedEffect = new CreateDelayedTriggeredAbilityEffect(delayed);
this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(delayedEffect, false, true));
}
private BroodOfCockroaches(final BroodOfCockroaches card) {
@ -45,36 +45,3 @@ public final class BroodOfCockroaches extends CardImpl {
return new BroodOfCockroaches(this);
}
}
class BroodOfCockroachesEffect extends OneShotEffect {
private static final String effectText = "at the beginning of the next end step, you lose 1 life and return {this} to your hand.";
BroodOfCockroachesEffect() {
super(Outcome.Benefit);
staticText = effectText;
}
private BroodOfCockroachesEffect(final BroodOfCockroachesEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedLifeLost =
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new LoseLifeSourceControllerEffect(1));
game.addDelayedTriggeredAbility(delayedLifeLost, source);
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return {this} to your hand.");
effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter()));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
@Override
public BroodOfCockroachesEffect copy() {
return new BroodOfCockroachesEffect(this);
}
}

View file

@ -0,0 +1,106 @@
package mage.cards.b;
import mage.ConditionalMana;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.condition.Condition;
import mage.abilities.keyword.FreerunningAbility;
import mage.abilities.mana.ColorlessManaAbility;
import mage.abilities.mana.ConditionalAnyColorManaAbility;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.Filter;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import java.util.UUID;
public class BrotherhoodHeadquarters extends CardImpl {
private static final Predicate<MageObject> predicate = new AbilityPredicate(FreerunningAbility.class);
public BrotherhoodHeadquarters(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// Add one colorless
this.addAbility(new ColorlessManaAbility());
// Add one mana of any color. Spend this mana only to cast an Assassin spell or a spell that has freerunning, or to activate an ability of an Assassin source.
this.addAbility(new ConditionalAnyColorManaAbility(1, new BrotherhoodHeadquartersManaBuilder()));
}
public BrotherhoodHeadquarters(BrotherhoodHeadquarters card) {
super(card);
}
@Override
public BrotherhoodHeadquarters copy() {
return new BrotherhoodHeadquarters(this);
}
class BrotherhoodHeadquartersManaBuilder extends ConditionalManaBuilder {
@Override
public String getRule() {
return "Spend this mana only to cast an Assassin spell or a spell that has freerunning, or to activate an ability of an Assassin source.";
}
@Override
public ConditionalMana build(Object... options) {
return new BrotherhoodHeadquartersConditionalMana(this.mana);
}
}
class BrotherhoodHeadquartersConditionalMana extends ConditionalMana {
public BrotherhoodHeadquartersConditionalMana(Mana mana) {
super(mana);
addCondition(new BrotherhoodHeadquartersAssassinSpellManaCondition());
addCondition(new BrotherhoodHeadquartersFreerunningManaCondition());
addCondition(new BrotherhoodHeadquartersAssassinSourceManaCondition());
setComparisonScope(Filter.ComparisonScope.Any);
}
}
class BrotherhoodHeadquartersAssassinSpellManaCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
if (source instanceof SpellAbility) {
Card card = game.getCard(source.getSourceId());
return card != null && card.hasSubtype(SubType.ASSASSIN, game);
}
return false;
}
}
class BrotherhoodHeadquartersFreerunningManaCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
MageObject sourceObject = game.getObject(source);
return sourceObject != null && predicate.apply(sourceObject, game);
}
}
class BrotherhoodHeadquartersAssassinSourceManaCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
if (source.isActivatedAbility()) {
MageObject object = game.getObject(source.getSourceId());
return object != null && object.hasSubtype(SubType.ASSASSIN, game);
}
return false;
}
}
}

View file

@ -0,0 +1,43 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CrewsVehicleSourceTriggeredAbility;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author notgreat
*/
public final class CanyonVaulter extends CardImpl {
public CanyonVaulter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.KOR);
this.subtype.add(SubType.PILOT);
this.power = new MageInt(3);
this.toughness = new MageInt(1);
// Whenever this creature saddles a Mount or crews a Vehicle during your main phase, that Mount or Vehicle gains flying until end of turn.
Effect effect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn);
effect.setText("that Mount or Vehicle gains flying until end of turn");
this.addAbility(new CrewsVehicleSourceTriggeredAbility(effect, true, true));
}
private CanyonVaulter(final CanyonVaulter card) {
super(card);
}
@Override
public CanyonVaulter copy() {
return new CanyonVaulter(this);
}
}

View file

@ -0,0 +1,69 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.costs.CompositeCost;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.MultipliedValue;
import mage.abilities.dynamicvalue.common.SavedDiscardValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.common.DiscardOneOrMoreCardsTriggeredAbility;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author Jmlundeen
*/
public final class CaptainHowlerSeaScourge extends CardImpl {
private static final DynamicValue powerValue = new MultipliedValue(SavedDiscardValue.MANY, 2);
public CaptainHowlerSeaScourge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.SHARK);
this.subtype.add(SubType.PIRATE);
this.power = new MageInt(5);
this.toughness = new MageInt(4);
// Ward--{2}, Pay 2 life.
CompositeCost cost = new CompositeCost(new ManaCostsImpl<>("{2}"), new PayLifeCost(2), "{2}, Pay 2 life");
this.addAbility(new WardAbility(cost, false));
// Whenever you discard one or more cards, target creature gets +2/+0 until end of turn for each card discarded this way. Whenever that creature deals combat damage to a player this turn, you draw a card.
Ability ability = new DiscardOneOrMoreCardsTriggeredAbility(
new BoostTargetEffect(powerValue, StaticValue.get(0))
.setText("target creature gets +2/+0 until end of turn for each card discarded this way")
);
ability.addEffect(new GainAbilityTargetEffect(
new DealsCombatDamageToAPlayerTriggeredAbility(
new DrawCardSourceControllerEffect(1), false
))
.setText("Whenever that creature deals combat damage to a player this turn, you draw a card")
);
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}
private CaptainHowlerSeaScourge(final CaptainHowlerSeaScourge card) {
super(card);
}
@Override
public CaptainHowlerSeaScourge copy() {
return new CaptainHowlerSeaScourge(this);
}
}

View file

@ -0,0 +1,60 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.counter.DistributeCountersEffect;
import mage.abilities.keyword.CrewAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.target.common.TargetPermanentAmount;
import java.util.UUID;
/**
* @author notgreat
*/
public final class CloudspireSkycycle extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("other target Vehicles and/or creatures you control");
static {
filter.add(AnotherPredicate.instance);
filter.add(Predicates.or(SubType.VEHICLE.getPredicate(), CardType.CREATURE.getPredicate()));
}
public CloudspireSkycycle(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}{W}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// When this Vehicle enters, distribute two +1/+1 counters among one or two other target Vehicles and/or creatures you control.
Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect());
ability.addTarget(new TargetPermanentAmount(2, 1, filter));
this.addAbility(ability);
// Crew 1
this.addAbility(new CrewAbility(1));
}
private CloudspireSkycycle(final CloudspireSkycycle card) {
super(card);
}
@Override
public CloudspireSkycycle copy() {
return new CloudspireSkycycle(this);
}
}

View file

@ -0,0 +1,116 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldWithCounterTargetEffect;
import mage.abilities.effects.common.SacrificeTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
import mage.constants.ComparisonType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.abilities.keyword.MenaceAbility;
import mage.abilities.keyword.DeathtouchAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.Filter;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author anonymous
*/
public final class CoalstokeGearhulk extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value 4 or less from a graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5));
}
public CoalstokeGearhulk(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}{B}{R}{R}");
this.subtype.add(SubType.CONSTRUCT);
this.power = new MageInt(5);
this.toughness = new MageInt(4);
// Menace
this.addAbility(new MenaceAbility());
// Deathtouch
this.addAbility(DeathtouchAbility.getInstance());
// When this creature enters, put target creature card with mana value 4 or less from a graveyard onto the battlefield under your control with a finality counter on it. That creature gains menace, deathtouch, and haste. At the beginnning of your next end step, exile that creature.
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(CounterType.FINALITY.createInstance()))
.setTriggerPhrase("When this creature enters, ");
ability.addEffect(new GainAbilityTargetEffect(new MenaceAbility())
.setText("That creature gains menace"));
ability.addEffect(new GainAbilityTargetEffect(DeathtouchAbility.getInstance())
.setText("deathtouch")
.concatBy(","));
ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance())
.setText("haste")
.concatBy(", and"));
ability.addEffect(new CoalstokeGearhulkEffect());
ability.addTarget(new TargetCardInGraveyard(filter));
this.addAbility(ability);
}
private CoalstokeGearhulk(final CoalstokeGearhulk card) {
super(card);
}
@Override
public CoalstokeGearhulk copy() {
return new CoalstokeGearhulk(this);
}
}
class CoalstokeGearhulkEffect extends OneShotEffect {
public CoalstokeGearhulkEffect() {
super(Outcome.Benefit);
this.staticText = "At the beginning of your next end step, exile that creature.";
}
public CoalstokeGearhulkEffect(final CoalstokeGearhulkEffect effect) {
super(effect);
}
@Override
public CoalstokeGearhulkEffect copy() {
return new CoalstokeGearhulkEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent == null) {
return false;
}
ExileTargetEffect exileEffect = new ExileTargetEffect("At the beginning of your next end step, exile " + permanent.getLogName());
exileEffect.setTargetPointer(new FixedTarget(permanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect, TargetController.YOU);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
}

View file

@ -1,6 +1,6 @@
package mage.cards.c;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.cards.*;
import mage.constants.CardType;
import mage.filter.StaticFilters;
@ -17,7 +17,7 @@ public final class Cultivate extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}");
// Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.
this.getSpellAbility().addEffect(new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(
this.getSpellAbility().addEffect(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS)));
}

View file

@ -0,0 +1,90 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.keyword.EmbalmAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
/**
*
* @author Jmlundeen
*/
public final class CurseclothWrappings extends CardImpl {
public static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Zombies you control");
static {
filter.add(SubType.ZOMBIE.getPredicate());
}
public CurseclothWrappings(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}{B}");
// Zombies you control get +1/+1.
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter)));
// {T}: Target creature card in your graveyard gains embalm until end of turn. The embalm cost is equal to its mana cost.
Ability ability = new SimpleActivatedAbility(
new CurseClothWrappingsEffect(),
new TapSourceCost()
);
ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card in your graveyard")));
this.addAbility(ability);
}
private CurseclothWrappings(final CurseclothWrappings card) {
super(card);
}
@Override
public CurseclothWrappings copy() {
return new CurseclothWrappings(this);
}
}
class CurseClothWrappingsEffect extends ContinuousEffectImpl {
public CurseClothWrappingsEffect() {
super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.staticText = "Target creature card in your graveyard gains embalm until end of turn. " +
"The embalm cost is equal to its mana cost. (Exile that card and pay its embalm cost: " +
"Create a token that's a copy of it, except it's a white Zombie in addition to its other types " +
"and has no mana cost. Embalm only as a sorcery.)";
}
public CurseClothWrappingsEffect(final CurseClothWrappingsEffect effect) {
super(effect);
}
@Override
public CurseClothWrappingsEffect copy() {
return new CurseClothWrappingsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getCard(getTargetPointer().getFirst(game, source));
if (card != null) {
EmbalmAbility embalmAbility = new EmbalmAbility(card.getManaCost(), card);
game.getState().addOtherAbility(card, embalmAbility);
return true;
}
return false;
}
}

View file

@ -10,21 +10,17 @@ import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.ExileFromGraveCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.watchers.common.SpellsCastWatcher;
import java.util.UUID;
@ -43,8 +39,8 @@ public final class Demilich extends CardImpl {
// This spell costs {U} less to cast for each instant and sorcery you've cast this turn.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(
new ManaCostsImpl<>("{U}"), DemilichValue.instance
)).addHint(new ValueHint("Instants and sorceries you've cast this turn", DemilichValue.instance)));
new ManaCostsImpl<>("{U}"), InstantAndSorceryCastThisTurn.YOU
)).addHint(InstantAndSorceryCastThisTurn.YOU.getHint()));
// Whenever Demilich attacks, exile up to one target instant or sorcery card from your graveyard. Copy it. You may cast the copy.
Ability ability = new AttacksTriggeredAbility(new ExileTargetCardCopyAndCastEffect(false).setText(
@ -66,34 +62,6 @@ public final class Demilich extends CardImpl {
}
}
enum DemilichValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
int spells = 0;
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
if (watcher != null) {
for (Spell spell : watcher.getSpellsCastThisTurn(sourceAbility.getControllerId())) {
if (spell.isInstantOrSorcery(game)) {
spells++;
}
}
}
return spells;
}
@Override
public DemilichValue copy() {
return instance;
}
@Override
public String getMessage() {
return "instant and sorcery spell you've cast this turn";
}
}
class DemilichPlayEffect extends AsThoughEffectImpl {
DemilichPlayEffect() {

View file

@ -0,0 +1,117 @@
package mage.cards.d;
import java.util.*;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.abilities.keyword.AffinityForArtifactsAbility;
import mage.abilities.keyword.CrewAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.EachTargetPointer;
/**
*
* @author Jmlundeen
*/
public final class DemonicJunker extends CardImpl {
public DemonicJunker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}{B}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(4);
this.toughness = new MageInt(3);
// Affinity for artifacts
this.addAbility(new AffinityForArtifactsAbility());
// When this Vehicle enters, for each player, destroy up to one target creature that player controls. If a creature you controlled was destroyed this way, put two +1/+1 counters on this Vehicle.
Ability ability = new EntersBattlefieldTriggeredAbility(new DemonicJunkerEffect()
.setTargetPointer(new EachTargetPointer()))
.setTriggerPhrase("When this Vehicle enters, ");
this.addAbility(ability.setTargetAdjuster(DemonicJunkerAdjuster.instance));
// Crew 2
this.addAbility(new CrewAbility(2));
}
private DemonicJunker(final DemonicJunker card) {
super(card);
}
@Override
public DemonicJunker copy() {
return new DemonicJunker(this);
}
}
enum DemonicJunkerAdjuster implements TargetAdjuster {
instance;
@Override
public void adjustTargets(Ability ability, Game game) {
ability.getTargets().clear();
for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
String playerName = ability.isControlledBy(playerId) ? "you" : player.getName();
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature controlled by " + playerName);
filter.add(new ControllerIdPredicate(playerId));
ability.addTarget(new TargetPermanent(0, 1, filter));
}
}
}
class DemonicJunkerEffect extends OneShotEffect {
DemonicJunkerEffect() {
super(Outcome.DestroyPermanent);
staticText = "for each player, destroy up to one target creature that player controls. If a creature you controlled was destroyed this way, put two +1/+1 counters on this Vehicle";
}
public DemonicJunkerEffect(final DemonicJunkerEffect effect) {
super(effect);
}
@Override
public DemonicJunkerEffect copy() {
return new DemonicJunkerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
boolean giveCounters = false;
for (UUID permanentId : getTargetPointer().getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent == null) {
continue;
}
if (permanent.destroy(source, game, false)) {
giveCounters = permanent.getControllerId().equals(source.getControllerId());
}
if (giveCounters) {
Permanent vehicle = game.getPermanent(source.getSourceId());
if (vehicle != null) {
vehicle.addCounters(CounterType.P1P1.createInstance(2), source.getControllerId(), source, game);
}
}
}
return true;
}
}

View file

@ -0,0 +1,57 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.CrewAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.XManaValueTargetAdjuster;
import java.util.UUID;
/**
* @author notgreat
*/
public final class DuneDrifter extends CardImpl {
private static final FilterCard filter = new FilterCard("artifact or creature card with mana value X or less from your graveyard");
static {
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate()));
}
public DuneDrifter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{X}{W}{B}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// When this Vehicle enters, return target artifact or creature card with mana value X or less from your graveyard to the battlefield.
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect());
ability.addTarget(new TargetCardInYourGraveyard(filter));
ability.setTargetAdjuster(new XManaValueTargetAdjuster(ComparisonType.OR_LESS));
this.addAbility(ability);
// Crew 2
this.addAbility(new CrewAbility(2));
}
private DuneDrifter(final DuneDrifter card) {
super(card);
}
@Override
public DuneDrifter copy() {
return new DuneDrifter(this);
}
}

View file

@ -61,17 +61,21 @@ class FanaticOfTheHarrowingEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
boolean flag = false;
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(source.getControllerId());
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
if (!player.discard(1, false, false, source, game).isEmpty()
&& player.equals(source.getControllerId())) {
&& playerId.equals(source.getControllerId())) {
flag = true;
}
}
game.processAction();
if (flag) {
game.getPlayer(source.getControllerId()).drawCards(1, source, game);
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.drawCards(1, source, game);
}
}
return true;
}

View file

@ -3,7 +3,7 @@ package mage.cards.f;
import mage.ObjectColor;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -35,7 +35,7 @@ public final class FlareOfCultivation extends CardImpl {
this.addAbility(new AlternativeCostSourceAbility(new SacrificeTargetCost(filter)).setRuleAtTheTop(true));
// Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.
this.getSpellAbility().addEffect(new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(
this.getSpellAbility().addEffect(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS)
));
}

View file

@ -9,7 +9,7 @@ import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.constants.Duration;
@ -56,7 +56,7 @@ public final class FlourishingBloomKin extends CardImpl {
// When Flourishing Bloom-Kin is turned face up, search your library for up to two Forest cards and reveal them.
// Put one of them onto the battlefield tapped and the other into your hand, then shuffle.
this.addAbility(new TurnedFaceUpSourceTriggeredAbility(
new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 2, filterForestCards))
new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 2, filterForestCards))
.setText("search your library for up to two Forest cards and reveal them. Put one of them onto the battlefield tapped and the other into your hand, then shuffle")));
}

View file

@ -16,6 +16,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import java.util.UUID;
@ -48,7 +49,7 @@ public final class GastalThrillroller extends CardImpl {
// {2}{R}, Discard a card: Return this card from your graveyard to the battlefield with a finality counter on it. Activate only as a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(
new ReturnSourceFromGraveyardToBattlefieldWithCounterEffect(
Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldWithCounterEffect(
CounterType.FINALITY.createInstance(), false
), new ManaCostsImpl<>("{2}{R}")
);

View file

@ -10,6 +10,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -39,6 +40,7 @@ public final class GnostroVoiceOfTheCrags extends CardImpl {
// {T}: Choose one. X is the number of spells you've cast this turn.
// Scry X.
Ability ability = new SimpleActivatedAbility(new GnostroVoiceOfTheCragsEffect(), new TapSourceCost());
ability.addHint(new ValueHint("Number of spells you've cast this turn", GnostroVoiceOfTheCragsValue.instance));
ability.getModes().setChooseText("choose one. X is the number of spells you've cast this turn.");
// Gnostro, Voice of the Crags deals X damage to target creature.
@ -104,7 +106,7 @@ class GnostroVoiceOfTheCragsEffect extends OneShotEffect {
if (player == null) {
return false;
}
return player.scry (
return player.scry(
GnostroVoiceOfTheCragsValue.instance.calculate(game, source, this), source, game
);
}

View file

@ -13,6 +13,7 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.DoWhenCostPaid;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
@ -34,9 +35,6 @@ import java.util.UUID;
* @author TheElk801
*/
public final class GristTheHungerTide extends CardImpl {
private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES);
public GristTheHungerTide(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{B}{G}");
@ -61,7 +59,10 @@ public final class GristTheHungerTide extends CardImpl {
), -2));
// 5: Each opponent loses life equal to the number of creature cards in your graveyard.
this.addAbility(new LoyaltyAbility(new LoseLifeOpponentsEffect(xValue).setText("each opponent loses life equal to the number of creature cards in your graveyard"), -5));
DynamicValue creatureCardsInGraveyard = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES);
this.addAbility(new LoyaltyAbility(new LoseLifeOpponentsEffect(creatureCardsInGraveyard)
.setText("each opponent loses life equal to the number of creature cards in your graveyard"), -5)
.addHint(new ValueHint("Creature cards in your graveyard", creatureCardsInGraveyard)));
}
private GristTheHungerTide(final GristTheHungerTide card) {

View file

@ -136,7 +136,7 @@ enum HakimLoreweaverCondition implements Condition {
.stream()
.map(game::getPermanent)
.filter(Objects::nonNull)
.anyMatch(permanent -> permanent.hasSubtype(SubType.AURA, game));
.noneMatch(permanent -> permanent.hasSubtype(SubType.AURA, game));
}
@Override

View file

@ -0,0 +1,74 @@
package mage.cards.h;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.ProtectionAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster;
import mage.target.targetpointer.EachTargetPointer;
import java.util.UUID;
/**
* @author PurpleCrowbar
*/
public final class HaythamKenway extends CardImpl {
private static final FilterCard filter = new FilterCard("Assassins");
private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(SubType.KNIGHT, "Knights");
static {
filter.add(SubType.ASSASSIN.getPredicate());
}
public HaythamKenway(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN, SubType.KNIGHT);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Protection from Assassins
this.addAbility(new ProtectionAbility(filter));
// Other Knights you control get +2/+2 and have protection from Assassins.
Ability ability = new SimpleStaticAbility(new BoostControlledEffect(
2, 2, Duration.WhileOnBattlefield, filter2, true
));
ability.addEffect(new GainAbilityControlledEffect(
new ProtectionAbility(filter), Duration.WhileOnBattlefield, filter2, true
).setText("and have protection from Assassins"));
this.addAbility(ability);
// When Haytham Kenway enters, for each opponent, exile up to one target creature that player controls until Haytham Kenway leaves the battlefield.
Ability ability2 = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect()
.setTargetPointer(new EachTargetPointer())
.setText("for each opponent, exile up to one target creature that player controls until {this} leaves the battlefield")
);
ability2.addTarget(new TargetCreaturePermanent(0, 1));
ability2.setTargetAdjuster(new ForEachOpponentTargetsAdjuster());
this.addAbility(ability2);
}
private HaythamKenway(final HaythamKenway card) {
super(card);
}
@Override
public HaythamKenway copy() {
return new HaythamKenway(this);
}
}

View file

@ -0,0 +1,40 @@
package mage.cards.i;
import mage.abilities.common.CantBeCounteredSourceAbility;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetNonlandPermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class InevitableDefeat extends CardImpl {
public InevitableDefeat(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}{W}{B}");
// This spell can't be countered.
this.addAbility(new CantBeCounteredSourceAbility());
// Exile target nonland permanent. Its controller loses 3 life and you gain 3 life.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addEffect(new LoseLifeTargetControllerEffect(3));
this.getSpellAbility().addEffect(new GainLifeEffect(3).concatBy("and"));
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
}
private InevitableDefeat(final InevitableDefeat card) {
super(card);
}
@Override
public InevitableDefeat copy() {
return new InevitableDefeat(this);
}
}

View file

@ -0,0 +1,40 @@
package mage.cards.j;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class JumboCactuar extends CardImpl {
public JumboCactuar(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}");
this.subtype.add(SubType.PLANT);
this.power = new MageInt(1);
this.toughness = new MageInt(7);
// 10,000 Needles -- Whenever this creature attacks, it gets +9999/+0 until end of turn.
this.addAbility(new AttacksTriggeredAbility(
new BoostSourceEffect(9999, 0, Duration.EndOfTurn, "it")
).withFlavorWord("10,000 Needles"));
}
private JumboCactuar(final JumboCactuar card) {
super(card);
}
@Override
public JumboCactuar copy() {
return new JumboCactuar(this);
}
}

View file

@ -1,6 +1,6 @@
package mage.cards.k;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.cards.*;
import mage.constants.CardType;
import mage.constants.SubType;
@ -19,7 +19,7 @@ public final class KodamasReach extends CardImpl {
this.subtype.add(SubType.ARCANE);
// Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.
this.getSpellAbility().addEffect(new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(
this.getSpellAbility().addEffect(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS)));
}

View file

@ -1,18 +1,12 @@
package mage.cards.l;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.IntPlusDynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.PlotAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.Game;
import mage.watchers.common.SpellsCastWatcher;
import java.util.Objects;
import java.util.UUID;
/**
@ -20,15 +14,14 @@ import java.util.UUID;
*/
public final class LockAndLoad extends CardImpl {
private static final DynamicValue xValue = new IntPlusDynamicValue(1, LockAndLoadValue.instance);
public LockAndLoad(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
// Draw a card, then draw a card for each other instant and sorcery spell you've cast this turn.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue)
this.getSpellAbility()
.addHint(InstantAndSorceryCastThisTurn.YOU.getHint())
.addEffect(new DrawCardSourceControllerEffect(InstantAndSorceryCastThisTurn.YOU)
.setText("Draw a card, then draw a card for each other instant and sorcery spell you've cast this turn"));
// Plot {3}{U}
this.addAbility(new PlotAbility("{3}{U}"));
}
@ -42,36 +35,3 @@ public final class LockAndLoad extends CardImpl {
return new LockAndLoad(this);
}
}
enum LockAndLoadValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
return watcher == null ? 0 :
watcher.getSpellsCastThisTurn(sourceAbility.getControllerId())
.stream()
.filter(Objects::nonNull)
.filter(s -> s.isInstantOrSorcery(game))
.filter(s -> !s.getSourceId().equals(sourceAbility.getSourceId())
|| s.getZoneChangeCounter(game) != sourceAbility.getSourceObjectZoneChangeCounter())
.mapToInt(x -> 1)
.sum();
}
@Override
public LockAndLoadValue copy() {
return this;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "Number of other instant and sorcery spell you've cast this turn";
}
}

View file

@ -2,7 +2,6 @@ package mage.cards.m;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -17,8 +16,8 @@ import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.target.Target;
import mage.target.TargetSpell;
import mage.util.CardUtil;
import java.util.UUID;
@ -69,13 +68,10 @@ enum MistfolkPredicate implements ObjectSourcePlayerPredicate<Spell> {
if (sourceObject == null || input.getObject() == null) {
return false;
}
for (SpellAbility spellAbility : input.getObject().getSpellAbilities()) {
for (Mode mode : spellAbility.getModes().values()) {
for (Target target : spellAbility.getTargets()) {
if (target.getTargets().contains(input.getSourceId())) {
return true;
}
}
if (CardUtil.getAllSelectedTargets(spellAbility, game).contains(input.getSourceId())) {
return true;
}
}
return false;

View file

@ -0,0 +1,50 @@
package mage.cards.m;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.abilities.mana.ActivateIfConditionManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class MoxJasper extends CardImpl {
private static final Condition condition = new PermanentsOnTheBattlefieldCondition(
new FilterControlledPermanent(SubType.DRAGON, "you control a Dragon")
);
private static final Hint hint = new ConditionHint(condition, "You control a Dragon");
public MoxJasper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}");
this.supertype.add(SuperType.LEGENDARY);
// {T}: Add one mana of any color. Activate only if you control a Dragon.
this.addAbility(new ActivateIfConditionManaAbility(
Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1),
new TapSourceCost(), condition
).addHint(hint));
}
private MoxJasper(final MoxJasper card) {
super(card);
}
@Override
public MoxJasper copy() {
return new MoxJasper(this);
}
}

View file

@ -32,7 +32,7 @@ public final class MuYanlingWindRider extends CardImpl {
= new FilterCreaturePermanent("creatures you control with flying");
static {
filter.add(new AbilityPredicate(FlyingAbility.class));
filter2.add(new AbilityPredicate(FlyingAbility.class));
}
public MuYanlingWindRider(UUID ownerId, CardSetInfo setInfo) {

View file

@ -0,0 +1,81 @@
package mage.cards.n;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.costs.common.DiscardHandCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.game.Game;
import mage.watchers.common.CastSpellLastTurnWatcher;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class NarsetJeskaiWaymaster extends CardImpl {
public NarsetJeskaiWaymaster(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.MONK);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// At the beginning of your end step, you may discard your hand. If you do, draw cards equal to the number of spells you've cast this turn.
this.addAbility(new BeginningOfEndStepTriggeredAbility(new DoIfCostPaid(
new DrawCardSourceControllerEffect(NarsetJeskaiWaymasterValue.instance), new DiscardHandCost()
)).addHint(NarsetJeskaiWaymasterValue.getHint()));
}
private NarsetJeskaiWaymaster(final NarsetJeskaiWaymaster card) {
super(card);
}
@Override
public NarsetJeskaiWaymaster copy() {
return new NarsetJeskaiWaymaster(this);
}
}
enum NarsetJeskaiWaymasterValue implements DynamicValue {
instance;
private static final Hint hint = new ValueHint("Spells you've cast this turn", instance);
public static Hint getHint() {
return hint;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class);
return watcher == null ? 0 : watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId());
}
@Override
public NarsetJeskaiWaymasterValue copy() {
return this;
}
@Override
public String getMessage() {
return "the number of spells you've cast this turn";
}
@Override
public String toString() {
return "1";
}
}

View file

@ -5,7 +5,7 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.cards.*;
import mage.constants.*;
import mage.filter.FilterCard;
@ -32,7 +32,7 @@ public final class NavigationOrb extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// {2}, {T}, Sacrifice Navigation Orb: Search your library for up to two basic land cards and/or Gate cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.
Ability ability = new SimpleActivatedAbility(new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(
Ability ability = new SimpleActivatedAbility(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 2, filter)), new GenericManaCost(2));
ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeSourceCost());

View file

@ -0,0 +1,99 @@
package mage.cards.n;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LandfallAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.SourceIsSpellCondition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.common.PayEnergyCost;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterPermanentCard;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
* @author PurpleCrowbar
*/
public final class NissaWorldsoulSpeaker extends CardImpl {
public NissaWorldsoulSpeaker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.ELF, SubType.DRUID);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Landfall -- Whenever a land you control enters, you get {E}{E}.
this.addAbility(new LandfallAbility(
new GetEnergyCountersControllerEffect(2), false
));
// You may pay eight {E} rather than pay the mana cost for permanent spells you cast.
this.addAbility(new SimpleStaticAbility(new NissaWorldsoulSpeakerEffect()));
}
private NissaWorldsoulSpeaker(final NissaWorldsoulSpeaker card) {
super(card);
}
@Override
public NissaWorldsoulSpeaker copy() {
return new NissaWorldsoulSpeaker(this);
}
}
class NissaWorldsoulSpeakerEffect extends ContinuousEffectImpl {
private static final FilterPermanentCard filter = new FilterPermanentCard();
private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(
new PayEnergyCost(8), SourceIsSpellCondition.instance, null, filter, true
);
public NissaWorldsoulSpeakerEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "You may pay eight {E} rather than pay the mana cost for permanent spells you cast";
}
private NissaWorldsoulSpeakerEffect(final NissaWorldsoulSpeakerEffect effect) {
super(effect);
}
@Override
public NissaWorldsoulSpeakerEffect copy() {
return new NissaWorldsoulSpeakerEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility);
return true;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}

View file

@ -4,7 +4,7 @@ package mage.cards.n;
import java.util.UUID;
import mage.abilities.condition.common.SpellMasteryCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -32,8 +32,8 @@ public final class NissasPilgrimage extends CardImpl {
// Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle.
// <i>Spell Mastery</i> &mdash; If there are two or more instant and/or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two.
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 3, filter)),
new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 2, filter)),
new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 3, filter)),
new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(new TargetCardInLibrary(0, 2, filter)),
SpellMasteryCondition.instance,
"Search your library for up to two basic Forest cards, reveal those cards, and put one onto the battlefield tapped and the rest into your hand. Then shuffle."
+ "<br><i>Spell mastery</i> &mdash; If there are two or more instant and/or sorcery cards in your graveyard, search your library for up to three basic Forest cards instead of two."));

View file

@ -6,6 +6,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.CascadeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -38,6 +39,7 @@ public final class NoiseMarine extends CardImpl {
new DamageTargetEffect(NoiseMarineValue.instance, "it")
);
ability.addTarget(new TargetAnyTarget());
ability.addHint(new ValueHint("Number of spells you've cast this turn", NoiseMarineValue.instance));
this.addAbility(ability.withFlavorWord("Sonic Blaster"));
}

View file

@ -75,7 +75,7 @@ class NotDeadAfterAllEffect extends OneShotEffect {
return false;
}
player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null);
Permanent permanent = game.getPermanent(card.getId());
if (permanent == null) {
return false;

View file

@ -1,7 +1,7 @@
package mage.cards.p;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.search.SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.cards.*;
import mage.constants.CardType;
@ -19,7 +19,7 @@ public final class Peregrination extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}");
// Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Shuffle, then scry 1.
this.getSpellAbility().addEffect(new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(
this.getSpellAbility().addEffect(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LANDS))
.setText("search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Shuffle"));
Effect effect = new ScryEffect(1);

View file

@ -8,7 +8,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.filter.common.FilterPermanentCard;
import mage.target.common.TargetCardInYourGraveyard;
/**
@ -17,16 +17,7 @@ import mage.target.common.TargetCardInYourGraveyard;
*/
public final class ProfoundJourney extends CardImpl {
private static final FilterCard filter = new FilterCard("permanent card from your graveyard");
static {
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate(),
CardType.ENCHANTMENT.getPredicate(),
CardType.LAND.getPredicate(),
CardType.PLANESWALKER.getPredicate()));
}
private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard");
public ProfoundJourney(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{W}{W}");

View file

@ -1,22 +1,24 @@
package mage.cards.r;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect;
import mage.abilities.keyword.CascadeAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TreasureToken;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.watchers.Watcher;
import mage.watchers.common.ManaPaidSourceWatcher;
@ -25,10 +27,18 @@ import java.util.Map;
import java.util.UUID;
/**
* @author Alex-Vasile
* @author Alex-Vasile, Susucr
*/
public class RainOfRiches extends CardImpl {
private static final FilterNonlandCard filter =
new FilterNonlandCard("The first spell you cast each turn that mana from a Treasure was spent to cast");
static {
filter.add(RainOfRichesPredicate.instance);
}
public RainOfRiches(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}");
@ -40,7 +50,7 @@ public class RainOfRiches extends CardImpl {
// You may cast it without paying its mana cost.
// Put the exiled cards on the bottom of your library in a random order.)
this.addAbility(
new SimpleStaticAbility(new RainOfRichesGainsCascadeEffect()),
new SimpleStaticAbility(new GainAbilityControlledSpellsEffect(new CascadeAbility(false), filter)),
new RainOfRichesWatcher()
);
}
@ -55,65 +65,20 @@ public class RainOfRiches extends CardImpl {
}
}
class RainOfRichesGainsCascadeEffect extends ContinuousEffectImpl {
private final Ability cascadeAbility = new CascadeAbility();
RainOfRichesGainsCascadeEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.staticText =
"The first spell you cast each turn that mana from a Treasure was spent to cast has cascade. " +
"<i>(When you cast the spell, exile cards from the top of your library until you exile a nonland card that costs less. " +
"You may cast it without paying its mana cost. " +
"Put the exiled cards on the bottom of your library in a random order.)</i>";
}
private RainOfRichesGainsCascadeEffect(final RainOfRichesGainsCascadeEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
RainOfRichesWatcher watcher = game.getState().getWatcher(RainOfRichesWatcher.class);
if (controller == null || watcher == null) {
return false;
}
for (StackObject stackObject : game.getStack()) {
// Only spells cast, so no copies of spells
if ((stackObject instanceof Spell)
&& !stackObject.isCopy()
&& stackObject.isControlledBy(source.getControllerId())) {
Spell spell = (Spell) stackObject;
if (FirstSpellCastWithTreasureCondition.instance.apply(game, source)) {
game.getState().addOtherAbility(spell.getCard(), cascadeAbility);
return true; // TODO: I think this should return here as soon as it finds the first one.
// If it should, change WildMageSorcerer to also return early.
}
}
}
return false;
}
@Override
public RainOfRichesGainsCascadeEffect copy() {
return new RainOfRichesGainsCascadeEffect(this);
}
}
enum FirstSpellCastWithTreasureCondition implements Condition {
enum RainOfRichesPredicate implements ObjectSourcePlayerPredicate<Card> {
instance;
@Override
public boolean apply(Game game, Ability source) {
if (game.getStack().isEmpty()) {
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
Permanent sourcePermanent = input.getSource().getSourcePermanentOrLKI(game);
if (sourcePermanent == null || !sourcePermanent.getControllerId().equals(input.getPlayerId())) {
return false;
}
RainOfRichesWatcher watcher = game.getState().getWatcher(RainOfRichesWatcher.class);
StackObject so = game.getStack().getFirst();
return watcher != null && RainOfRichesWatcher.checkSpell(so, game);
Card card = input.getObject();
return watcher != null
&& card instanceof StackObject
&& watcher.checkSpell((Spell) card, game);
}
}
@ -127,7 +92,7 @@ class RainOfRichesWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.CAST_SPELL) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpell(event.getSourceId());
@ -148,13 +113,15 @@ class RainOfRichesWatcher extends Watcher {
super.reset();
}
static boolean checkSpell(StackObject stackObject, Game game) {
boolean checkSpell(StackObject stackObject, Game game) {
if (stackObject.isCopy()
|| !(stackObject instanceof Spell)) {
return false;
}
RainOfRichesWatcher watcher = game.getState().getWatcher(RainOfRichesWatcher.class);
return watcher.playerMap.containsKey(stackObject.getControllerId())
&& watcher.playerMap.get(stackObject.getControllerId()).refersTo(((Spell) stackObject).getMainCard(), game);
if (playerMap.containsKey(stackObject.getControllerId())) {
return playerMap.get(stackObject.getControllerId()).refersTo(((Spell) stackObject).getMainCard(), game);
} else {
return ManaPaidSourceWatcher.getTreasurePaid(stackObject.getId(), game) >= 1;
}
}
}

View file

@ -10,9 +10,8 @@ import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageMultiEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@ -34,9 +33,7 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetAnyTargetAmount;
import mage.util.CardUtil;
import mage.watchers.common.SpellsCastWatcher;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@ -67,9 +64,11 @@ public final class RalLeylineProdigy extends CardImpl {
// Ral, Leyline Prodigy enters the battlefield with an additional loyalty counter on him for each instant and sorcery spell you've cast this turn.
this.addAbility(new EntersBattlefieldAbility(
new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(), RalLeylineProdigyValue.instance, false)
.setText("with an additional loyalty counter on him for each instant and sorcery spell you've cast this turn")
));
new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(), InstantAndSorceryCastThisTurn.YOU,
false)
.setText("with an additional loyalty counter on him for each instant and sorcery spell you've cast this turn"))
.addHint(InstantAndSorceryCastThisTurn.YOU.getHint())
);
// +1: Until your next turn, instant and sorcery spells you cast cost {1} less to cast.
this.addAbility(new LoyaltyAbility(new RalLeylineProdigyCostReductionEffect(), 1));
@ -126,35 +125,6 @@ class RalLeylineProdigyCostReductionEffect extends OneShotEffect {
}
}
enum RalLeylineProdigyValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
if (watcher == null) {
return 0;
}
return watcher
.getSpellsCastThisTurn(sourceAbility.getControllerId())
.stream()
.filter(Objects::nonNull)
.filter(spell -> spell.isInstantOrSorcery(game))
.mapToInt(spell -> 1)
.sum();
}
@Override
public RalLeylineProdigyValue copy() {
return instance;
}
@Override
public String getMessage() {
return "instant and sorcery spell you've cast this turn";
}
}
class RalLeylineProdigyMinusEightEffect extends OneShotEffect {
RalLeylineProdigyMinusEightEffect() {

View file

@ -2,6 +2,7 @@ package mage.cards.r;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.constants.Pronoun;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -45,7 +46,8 @@ public final class RalMonsoonMage extends CardImpl {
// Whenever you cast an instant or sorcery spell during your turn, flip a coin. If you lose the flip, Ral, Monsoon Mage deals 1 damage to you. If you win the flip, you may exile Ral. If you do, return him to the battlefield transformed under his owner control.
this.addAbility(new TransformAbility());
this.addAbility(new RalMonsoonMageTriggeredAbility());
this.addAbility(new RalMonsoonMageTriggeredAbility()
.addHint(InstantAndSorceryCastThisTurn.YOU.getHint()));
}
private RalMonsoonMage(final RalMonsoonMage card) {

View file

@ -1,18 +1,17 @@
package mage.cards.r;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.Ability;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CrewsVehicleSourceTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
@ -30,7 +29,14 @@ public final class RecklessVelocitaur extends CardImpl {
this.toughness = new MageInt(3);
// Whenever this creature saddles a Mount or crews a Vehicle during your main phase, that Mount or Vehicle gets +2/+0 and gains trample until end of turn.
this.addAbility(new RecklessVelocitaurTriggeredAbility());
Effect boostEffect = new BoostTargetEffect(2, 0, Duration.EndOfTurn)
.setText("that Mount or Vehicle gets +2/+0");
Effect abilityGainEffect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)
.setText("and gains trample until end of turn");
Ability ability = new CrewsVehicleSourceTriggeredAbility(boostEffect, true, true);
ability.addEffect(abilityGainEffect);
this.addAbility(ability);
}
private RecklessVelocitaur(final RecklessVelocitaur card) {
@ -42,43 +48,3 @@ public final class RecklessVelocitaur extends CardImpl {
return new RecklessVelocitaur(this);
}
}
class RecklessVelocitaurTriggeredAbility extends TriggeredAbilityImpl {
RecklessVelocitaurTriggeredAbility() {
super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0).setText("that Mount or Vehicle gets +2/+0"));
this.addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance()).setText("and gains trample until end of turn"));
this.setTriggerPhrase("Whenever {this} saddles a Mount or crews a Vehicle during your main phase, ");
}
private RecklessVelocitaurTriggeredAbility(final RecklessVelocitaurTriggeredAbility ability) {
super(ability);
}
@Override
public RecklessVelocitaurTriggeredAbility copy() {
return new RecklessVelocitaurTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
switch (event.getType()) {
case SADDLED_MOUNT:
case CREWED_VEHICLE:
return true;
default:
return false;
}
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!this.isControlledBy(game.getActivePlayerId())
|| !game.isMainPhase()
|| !event.getTargetId().equals(this.getSourceId())) {
return false;
}
this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId()));
return true;
}
}

View file

@ -2,23 +2,17 @@ package mage.cards.r;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.abilities.hint.Hint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import mage.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
@ -40,7 +34,7 @@ public final class RionyaFireDancer extends CardImpl {
new RionyaFireDancerEffect()
);
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL));
this.addAbility(ability.addHint(RionyaFireDancerHint.instance), new RionyaFireDancerWatcher());
this.addAbility(ability.addHint(InstantAndSorceryCastThisTurn.YOU.getHint()));
}
private RionyaFireDancer(final RionyaFireDancer card) {
@ -53,21 +47,6 @@ public final class RionyaFireDancer extends CardImpl {
}
}
enum RionyaFireDancerHint implements Hint {
instance;
@Override
public String getText(Game game, Ability ability) {
return "Instants and sorceries you've cast this turn: "
+ RionyaFireDancerWatcher.getValue(ability.getControllerId(), game);
}
@Override
public RionyaFireDancerHint copy() {
return instance;
}
}
class RionyaFireDancerEffect extends OneShotEffect {
RionyaFireDancerEffect() {
@ -90,41 +69,10 @@ class RionyaFireDancerEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(
source.getControllerId(), null, true,
RionyaFireDancerWatcher.getValue(source.getControllerId(), game) + 1
InstantAndSorceryCastThisTurn.YOU.calculate(game, source, this) + 1
);
effect.apply(game, source);
effect.exileTokensCreatedAtNextEndStep(game, source);
return true;
}
}
class RionyaFireDancerWatcher extends Watcher {
private final Map<UUID, Integer> playerMap = new HashMap<>();
RionyaFireDancerWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell != null && spell.isInstantOrSorcery(game)) {
playerMap.compute(spell.getControllerId(), CardUtil::setOrIncrementValue);
}
}
@Override
public void reset() {
super.reset();
playerMap.clear();
}
static int getValue(UUID playerId, Game game) {
RionyaFireDancerWatcher watcher = game.getState().getWatcher(RionyaFireDancerWatcher.class);
return watcher == null ? 0 : watcher.playerMap.getOrDefault(playerId, 0);
}
}

View file

@ -0,0 +1,38 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.common.LandfallAbility;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class SazhsChocobo extends CardImpl {
public SazhsChocobo(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
this.subtype.add(SubType.BIRD);
this.power = new MageInt(0);
this.toughness = new MageInt(1);
// Landfall -- Whenever a land you control enters, put a +1/+1 counter on this creature.
this.addAbility(new LandfallAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())));
}
private SazhsChocobo(final SazhsChocobo card) {
super(card);
}
@Override
public SazhsChocobo copy() {
return new SazhsChocobo(this);
}
}

View file

@ -8,6 +8,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.effects.common.SacrificeAllEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -22,7 +23,6 @@ import java.util.UUID;
* @author jimga150
*/
public final class SeasonOfLoss extends CardImpl {
public SeasonOfLoss(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
@ -41,9 +41,11 @@ public final class SeasonOfLoss extends CardImpl {
this.getSpellAbility().addMode(mode2.withPawPrintValue(2));
// {P}{P}{P} -- Each opponent loses X life, where X is the number of creature cards in your graveyard.
Mode mode3 = new Mode(new LoseLifeOpponentsEffect(new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES))
DynamicValue creatureCardsInGraveyard = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES);
Mode mode3 = new Mode(new LoseLifeOpponentsEffect(creatureCardsInGraveyard)
.setText("Each opponent loses X life, where X is the number of creature cards in your graveyard."));
this.getSpellAbility().addMode(mode3.withPawPrintValue(3));
this.getSpellAbility().addHint(new ValueHint("Creature cards in your graveyard", creatureCardsInGraveyard));
}
private SeasonOfLoss(final SeasonOfLoss card) {

View file

@ -0,0 +1,64 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class ShikoParagonOfTheWay extends CardImpl {
private static final FilterCard filter = new FilterNonlandCard("nonland card with mana value 3 or less");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4));
}
public ShikoParagonOfTheWay(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.SPIRIT);
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(4);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// When Shiko enters, exile target nonland card with mana value 3 or less from your graveyard. Copy it, then you may cast the copy without paying its mana cost.
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetCardCopyAndCastEffect(true)
.setText("exile target nonland card with mana value 3 or less from your graveyard. " +
"Copy it, then you may cast the copy without paying its mana cost"));
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability);
}
private ShikoParagonOfTheWay(final ShikoParagonOfTheWay card) {
super(card);
}
@Override
public ShikoParagonOfTheWay copy() {
return new ShikoParagonOfTheWay(this);
}
}

View file

@ -1,6 +1,7 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CastSourceTriggeredAbility;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@ -17,7 +18,6 @@ import mage.game.stack.Spell;
import mage.target.common.TargetCreaturePermanent;
import mage.watchers.common.SpellsCastWatcher;
import java.util.Objects;
import java.util.UUID;
/**
@ -29,7 +29,8 @@ public final class ShowOfConfidence extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
// When you cast this spell, copy it for each other instant or sorcery spell you've cast this turn. You may choose new targets for the copies.
this.addAbility(new CastSourceTriggeredAbility(new ShowOfConfidenceEffect()));
this.addAbility(new CastSourceTriggeredAbility(new ShowOfConfidenceEffect())
.addHint(InstantAndSorceryCastThisTurn.YOU.getHint()));
// Put a +1/+1 counter on target creature. It gains vigilance until end of turn.
this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
@ -73,14 +74,7 @@ class ShowOfConfidenceEffect extends OneShotEffect {
if (spell == null || watcher == null) {
return false;
}
int copies = watcher.getSpellsCastThisTurn(source.getControllerId())
.stream()
.filter(Objects::nonNull)
.filter(spell1 -> spell1.isInstantOrSorcery(game))
.filter(s -> !s.getSourceId().equals(source.getSourceId())
|| s.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter())
.mapToInt(x -> 1)
.sum();
int copies = InstantAndSorceryCastThisTurn.YOU.calculate(game, source, this) - 1;
if (copies > 0) {
spell.createCopyOnStack(game, source, source.getControllerId(), true, copies);
}

View file

@ -0,0 +1,45 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class SkirmishRhino extends CardImpl {
public SkirmishRhino(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}{G}");
this.subtype.add(SubType.RHINO);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Trample
this.addAbility(TrampleAbility.getInstance());
// When this creature enters, each opponent loses 2 life and you gain 2 life.
Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeOpponentsEffect(2));
ability.addEffect(new GainLifeEffect(2).concatBy("and"));
this.addAbility(ability);
}
private SkirmishRhino(final SkirmishRhino card) {
super(card);
}
@Override
public SkirmishRhino copy() {
return new SkirmishRhino(this);
}
}

View file

@ -0,0 +1,91 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class SmileAtDeath extends CardImpl {
private static final FilterCard filter = new FilterCreatureCard("creature cards with power 2 or less");
static {
filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3));
}
public SmileAtDeath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}");
// At the beginning of your upkeep, return up to two target creature cards with power 2 or less from your graveyard to the battlefield. Put a +1/+1 counter on each of those creatures.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new SmileAtDeathEffect());
ability.addTarget(new TargetCardInYourGraveyard(0, 2, filter));
this.addAbility(ability);
}
private SmileAtDeath(final SmileAtDeath card) {
super(card);
}
@Override
public SmileAtDeath copy() {
return new SmileAtDeath(this);
}
}
class SmileAtDeathEffect extends OneShotEffect {
SmileAtDeathEffect() {
super(Outcome.Benefit);
staticText = "return up to two target creature cards with power 2 or less " +
"from your graveyard to the battlefield. Put a +1/+1 counter on each of those creatures";
}
private SmileAtDeathEffect(final SmileAtDeathEffect effect) {
super(effect);
}
@Override
public SmileAtDeathEffect copy() {
return new SmileAtDeathEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source));
cards.retainZone(Zone.GRAVEYARD, game);
if (cards.isEmpty()) {
return false;
}
player.moveCards(cards, Zone.BATTLEFIELD, source, game);
for (UUID cardId : cards) {
Optional.ofNullable(cardId)
.map(game::getPermanent)
.ifPresent(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(), source, game));
}
return true;
}
}

View file

@ -2,8 +2,10 @@ package mage.cards.s;
import java.util.UUID;
import mage.Mana;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -14,13 +16,14 @@ import mage.filter.StaticFilters;
* @author fireshoes
*/
public final class SongsOfTheDamned extends CardImpl {
public SongsOfTheDamned(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
// Add {B} for each creature card in your graveyard.
DynamicManaEffect effect = new DynamicManaEffect(Mana.BlackMana(1), new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE));
DynamicValue creatureCardsInGraveyard = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES);
DynamicManaEffect effect = new DynamicManaEffect(Mana.BlackMana(1), creatureCardsInGraveyard);
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addHint(new ValueHint("Creature cards in your graveyard", creatureCardsInGraveyard));
}
private SongsOfTheDamned(final SongsOfTheDamned card) {

View file

@ -10,6 +10,7 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.Cost;
import mage.abilities.dynamicvalue.common.InstantAndSorceryCastThisTurn;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@ -24,14 +25,9 @@ import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.util.CardUtil;
import mage.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
@ -76,7 +72,7 @@ public final class SorcererClass extends CardImpl {
StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY,
false, SetTargetPointer.SPELL
), 3
)), new SorcererClassWatcher());
)).addHint(InstantAndSorceryCastThisTurn.YOU.getHint()));
}
private SorcererClass(final SorcererClass card) {
@ -151,7 +147,7 @@ class SorcererClassEffect extends OneShotEffect {
if (spell == null) {
return false;
}
int count = SorcererClassWatcher.spellCount(source.getControllerId(), game);
int count = InstantAndSorceryCastThisTurn.YOU.calculate(game, source, this);
if (count < 1) {
return false;
}
@ -165,35 +161,3 @@ class SorcererClassEffect extends OneShotEffect {
return true;
}
}
class SorcererClassWatcher extends Watcher {
private final Map<UUID, Integer> spellMap = new HashMap<>();
SorcererClassWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell == null || !spell.isInstantOrSorcery(game)) {
return;
}
spellMap.compute(spell.getControllerId(), CardUtil::setOrIncrementValue);
}
@Override
public void reset() {
spellMap.clear();
super.reset();
}
static int spellCount(UUID playerId, Game game) {
SorcererClassWatcher watcher = game.getState().getWatcher(SorcererClassWatcher.class);
return watcher != null ? watcher.spellMap.getOrDefault(playerId, 0) : 0;
}
}

View file

@ -0,0 +1,51 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.StormAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.filter.common.FilterCreaturePermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class StormscaleScion extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.DRAGON, "Dragons");
public StormscaleScion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Other Dragons you control get +1/+1.
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
1, 1, Duration.WhileOnBattlefield, filter, true
)));
// Storm
this.addAbility(new StormAbility());
}
private StormscaleScion(final StormscaleScion card) {
super(card);
}
@Override
public StormscaleScion copy() {
return new StormscaleScion(this);
}
}

View file

@ -0,0 +1,78 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealtDamageToSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.common.counter.AddCountersPlayersEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
import java.util.UUID;
public class StrongTheBrutishThespian extends CardImpl {
public StrongTheBrutishThespian(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
this.addSuperType(SuperType.LEGENDARY);
this.addSubType(SubType.MUTANT);
this.addSubType(SubType.BERSERKER);
this.power = new MageInt(7);
this.toughness = new MageInt(7);
// Ward {2}
this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}")));
// Enrage - Whenever strong is dealt damage, you get three rad counters and put three +1/+1 counters on Strong.
Ability enrageAbility = new DealtDamageToSourceTriggeredAbility(new AddCountersPlayersEffect(CounterType.RAD.createInstance(3), TargetController.YOU), false, true);
enrageAbility.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)).setText("put three +1/+1 counters on Strong"));
this.addAbility(enrageAbility);
// You gain life rather than lose life from radiation.
Ability healAbility = new SimpleStaticAbility(new StrongTheBrutishThespianHealEffect().setText("You gain life rather than lose life from radiation."));
this.addAbility(healAbility);
}
public StrongTheBrutishThespian(StrongTheBrutishThespian card) {
super(card);
}
@Override
public StrongTheBrutishThespian copy() {
return new StrongTheBrutishThespian(this);
}
class StrongTheBrutishThespianHealEffect extends ContinuousRuleModifyingEffectImpl {
protected StrongTheBrutishThespianHealEffect() {
super(Duration.Custom, Outcome.Benefit);
}
public StrongTheBrutishThespianHealEffect(StrongTheBrutishThespianHealEffect effect) {
super(effect);
}
@Override
public StrongTheBrutishThespianHealEffect copy() {
return new StrongTheBrutishThespianHealEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.RADIATION_GAIN_LIFE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(source.getControllerId());
}
}
}

View file

@ -0,0 +1,78 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.common.SagaAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SagaChapter;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterOpponentsCreaturePermanent;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.target.common.TargetOpponentsCreaturePermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class SummonShiva extends CardImpl {
private static final FilterPermanent filter
= new FilterOpponentsCreaturePermanent("tapped creature your opponents control");
static {
filter.add(TappedPredicate.TAPPED);
}
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 1);
private static final Hint hint = new ValueHint("Tapped creatures your opponents control", xValue);
public SummonShiva(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{U}{U}");
this.subtype.add(SubType.SAGA);
this.subtype.add(SubType.ELEMENTAL);
this.power = new MageInt(4);
this.toughness = new MageInt(5);
// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)
SagaAbility sagaAbility = new SagaAbility(this);
// I, II -- Heavenly Strike -- Tap target creature an opponent controls. Put a stun counter on it.
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, ability -> {
ability.addEffect(new TapTargetEffect());
ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance())
.setText("Put a stun counter on it"));
ability.addTarget(new TargetOpponentsCreaturePermanent());
ability.withFlavorWord("Heavenly Strike");
});
// III -- Diamond Dust -- Draw a card for each tapped creature your opponents control.
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> {
ability.addEffect(new DrawCardSourceControllerEffect(xValue));
ability.withFlavorWord("Diamond Dust");
ability.addHint(hint);
});
this.addAbility(sagaAbility);
}
private SummonShiva(final SummonShiva card) {
super(card);
}
@Override
public SummonShiva copy() {
return new SummonShiva(this);
}
}

View file

@ -0,0 +1,89 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.PreventDamageToControllerEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class SuppressorSkyguard extends CardImpl {
public SuppressorSkyguard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
this.addSubType(SubType.HUMAN);
this.addSubType(SubType.KNIGHT);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// whenever a player attacks you, if that player has another opponent who isn't being attacked, prevent all combat damage that would be dealt to you this combat
this.addAbility(new SuppressorSkyguardTriggerAttackYou());
}
public SuppressorSkyguard(SuppressorSkyguard card) {
super(card);
}
@Override
public SuppressorSkyguard copy() {
return new SuppressorSkyguard(this);
}
class SuppressorSkyguardTriggerAttackYou extends TriggeredAbilityImpl {
SuppressorSkyguardTriggerAttackYou() {
super(Zone.BATTLEFIELD, new PreventDamageToControllerEffect(Duration.EndOfCombat, true, false, Integer.MAX_VALUE), false);
this.setTriggerPhrase("whenever a player attacks you, ");
}
private SuppressorSkyguardTriggerAttackYou(final SuppressorSkyguardTriggerAttackYou ability) {
super(ability);
}
@Override
public SuppressorSkyguardTriggerAttackYou copy() {
return new SuppressorSkyguardTriggerAttackYou(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return getControllerId().equals(event.getTargetId());
}
@Override
public boolean checkInterveningIfClause(Game game) {
UUID activePlayerId = game.getActivePlayerId();
Set<UUID> opponents = game.getOpponents(activePlayerId);
game.getCombat()
.getGroups()
.stream()
.map(CombatGroup::getDefenderId)
.filter(Objects::nonNull)
.distinct()
.forEach(opponents::remove);
return !opponents.isEmpty();
}
@Override
public String getRule() {
return "Whenever a player attacks you, if that player has another opponent who isn't being attacked, prevent all combat damage that would be dealt to you this combat.";
}
}
}

View file

@ -0,0 +1,68 @@
package mage.cards.t;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.DoWhenCostPaid;
import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class TerraHeraldOfHope extends CardImpl {
private static final FilterCard filter = new FilterCreatureCard("creature card with power 3 or less from your graveyard");
static {
filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4));
}
public TerraHeraldOfHope(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Trance -- At the beginning of combat on your turn, mill two cards. Terra gains flying until end of turn.
Ability ability = new BeginningOfCombatTriggeredAbility(new MillCardsControllerEffect(2));
ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn));
this.addAbility(ability.withFlavorWord("Trance"));
// Whenever Terra deals combat damage to a player, you may pay {2}. When you do, return target creature card with power 3 or less from your graveyard to the battlefield tapped.
ReflexiveTriggeredAbility reflexiveAbility = new ReflexiveTriggeredAbility(
new ReturnFromGraveyardToBattlefieldTargetEffect(true), false
);
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(
new DoWhenCostPaid(reflexiveAbility, new GenericManaCost(2), "Pay {2}?")
));
}
private TerraHeraldOfHope(final TerraHeraldOfHope card) {
super(card);
}
@Override
public TerraHeraldOfHope copy() {
return new TerraHeraldOfHope(this);
}
}

View file

@ -0,0 +1,92 @@
package mage.cards.t;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.CardsLeaveGraveyardTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.token.ZombieDruidToken;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class TevalTheBalancedScale extends CardImpl {
public TevalTheBalancedScale(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.SPIRIT);
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Teval attacks, mill three cards. Then you may return a land card from your graveyard to the battlefield tapped.
Ability ability = new AttacksTriggeredAbility(new MillCardsControllerEffect(3));
ability.addEffect(new TevalTheBalancedScaleEffect());
this.addAbility(ability);
// Whenever one or more cards leave your graveyard, create a 2/2 black Zombie Druid creature token.
this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new CreateTokenEffect(new ZombieDruidToken())));
}
private TevalTheBalancedScale(final TevalTheBalancedScale card) {
super(card);
}
@Override
public TevalTheBalancedScale copy() {
return new TevalTheBalancedScale(this);
}
}
class TevalTheBalancedScaleEffect extends OneShotEffect {
TevalTheBalancedScaleEffect() {
super(Outcome.Benefit);
staticText = "Then you may return a land card from your graveyard to the battlefield tapped";
}
private TevalTheBalancedScaleEffect(final TevalTheBalancedScaleEffect effect) {
super(effect);
}
@Override
public TevalTheBalancedScaleEffect copy() {
return new TevalTheBalancedScaleEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
TargetCard target = new TargetCardInYourGraveyard(
0, 1, StaticFilters.FILTER_CARD_LAND, true
);
player.choose(outcome, player.getGraveyard(), target, source, game);
Card card = game.getCard(target.getFirstTarget());
return card != null && player.moveCards(
card, Zone.BATTLEFIELD, source, game, true,
false, false, null
);
}
}

View file

@ -0,0 +1,105 @@
package mage.cards.t;
import java.util.UUID;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.SagaAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.counter.TimeTravelEffect;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SagaChapter;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TheGirlInTheFireplaceHorseToken;
import mage.game.permanent.token.TheGirlInTheFireplaceHumanNobleToken;
import mage.filter.common.FilterControlledCreaturePermanent;
/**
*
* @author padfoot
*/
public final class TheGirlInTheFireplace extends CardImpl {
public TheGirlInTheFireplace(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
this.subtype.add(SubType.SAGA);
// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)
SagaAbility sagaAbility = new SagaAbility(this);
// I -- Create a 1/1 white Human Noble creature token with vanishing 3 and "Prevent all damage that would be dealt to this creature."
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new TheGirlInTheFireplaceHumanNobleToken()));
// II -- Create a 2/2 white Horse creature token with "Doctors you control have horsemanship."
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new CreateTokenEffect(new TheGirlInTheFireplaceHorseToken())
.withAdditionalRules(" <i>(They can't be blocked except by creatures with horsemanship.)</i>"));
// III -- Whenever a creature you control deals combat damage to a player this turn, time travel.
sagaAbility.addChapterEffect(
this, SagaChapter.CHAPTER_III,
new CreateDelayedTriggeredAbilityEffect(
new TheGirlInTheFireplaceTriggeredAbility()
)
);
this.addAbility(sagaAbility);
}
private TheGirlInTheFireplace(final TheGirlInTheFireplace card) {
super(card);
}
@Override
public TheGirlInTheFireplace copy() {
return new TheGirlInTheFireplace(this);
}
}
class TheGirlInTheFireplaceTriggeredAbility extends DelayedTriggeredAbility {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
public TheGirlInTheFireplaceTriggeredAbility() {
super(new TimeTravelEffect(), Duration.EndOfTurn, false);
}
private TheGirlInTheFireplaceTriggeredAbility(TheGirlInTheFireplaceTriggeredAbility ability) {
super(ability);
}
@Override
public TheGirlInTheFireplaceTriggeredAbility copy() {
return new TheGirlInTheFireplaceTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!((DamagedPlayerEvent) event).isCombatDamage()) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (!filter.match(permanent, getControllerId(), this, game)) {
return false;
}
return true;
}
@Override
public String getRule() {
return "Whenever a creature you control deals combat damage to a player this turn, time travel.";
}
}

View file

@ -0,0 +1,62 @@
package mage.cards.t;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.MyTurnCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.TapSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.DeathtouchAbility;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class Tonberry extends CardImpl {
public Tonberry(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
this.subtype.add(SubType.SALAMANDER);
this.subtype.add(SubType.HORROR);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
// This creature enters tapped with a stun counter on it.
Ability ability = new EntersBattlefieldAbility(
new TapSourceEffect(true), "tapped with a stun counter on it"
);
ability.addEffect(new AddCountersSourceEffect(CounterType.STUN.createInstance()));
this.addAbility(ability);
// Chef's Knife -- During your turn, this creature has first strike and deathtouch.
ability = new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()),
MyTurnCondition.instance, "during your turn, this creature has first strike"
));
ability.addEffect(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(DeathtouchAbility.getInstance()),
MyTurnCondition.instance, "and deathtouch"
));
this.addAbility(ability.withFlavorWord("Chef's Knife"));
}
private Tonberry(final Tonberry card) {
super(card);
}
@Override
public Tonberry copy() {
return new Tonberry(this);
}
}

View file

@ -0,0 +1,32 @@
package mage.cards.v;
import mage.abilities.effects.common.search.SearchLibraryPutOntoBattlefieldTappedRestInHandEffect;
import mage.abilities.keyword.FreerunningAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.StaticFilters;
import mage.target.common.TargetCardInLibrary;
import java.util.UUID;
public class ViewpointSynchronization extends CardImpl {
public ViewpointSynchronization(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}");
this.addAbility(new FreerunningAbility("{2}{G}"));
// Search your library for up to three basic land cards and reveal them. Put two of them onto the battlefield tapped and the other in your hand, then shuffle.
this.getSpellAbility().addEffect(new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(
new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_BASIC_LANDS), 2));
}
public ViewpointSynchronization(ViewpointSynchronization card) {
super(card);
}
@Override
public ViewpointSynchronization copy() {
return new ViewpointSynchronization(this);
}
}

View file

@ -4,11 +4,10 @@ import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.PayEnergyCost;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.SacrificeTargetEffect;
import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HexproofBaseAbility;
@ -138,10 +137,13 @@ class VolatileStormdrakeEffect extends OneShotEffect {
game.addEffect(effect, source);
game.processAction();
controller.addCounters(CounterType.ENERGY.createInstance(4), controller.getId(), source, game);
new DoIfCostPaid(
null, new SacrificeTargetEffect("", controller.getId()),
new PayEnergyCost(targetPermanent.getManaValue()), true
).apply(game, source);
Cost cost = new PayEnergyCost(targetPermanent.getManaValue());
if (cost.canPay(source, source, controller.getId(), game) &&
controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + " to prevent sacrifice?", source, game) &&
cost.pay(source, game, source, controller.getId(), false)) {
return true;
}
targetPermanent.sacrifice(source, game);
return true;
}
}

View file

@ -4,6 +4,7 @@ import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.CascadeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -36,6 +37,7 @@ public final class VolcanicTorrent extends CardImpl {
// Volcanic Torrent deals X damage to each creature and planeswalker your opponents control, where X is the number of spells you've cast this turn.
this.getSpellAbility().addEffect(new DamageAllEffect(VolcanicTorrentValue.instance, filter));
this.getSpellAbility().addHint(new ValueHint("Number of spells you've cast this turn", VolcanicTorrentValue.instance));
}
private VolcanicTorrent(final VolcanicTorrent card) {

View file

@ -77,7 +77,8 @@ enum CanTargetOnlyWallsPredicate implements Predicate<MageObject> {
return false;
}
boolean canTargetOnlyWalls = false;
for (Mode mode : stackObject.getStackAbility().getModes().values()) {
for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
Mode mode = stackObject.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
Filter filter = target.getFilter();
if (!(filter instanceof FilterPermanent)) {

View file

@ -4,6 +4,7 @@ import mage.MageInt;
import mage.abilities.common.ZoneChangeTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.GetXValue;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.CyclingAbility;
import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl;
@ -58,7 +59,7 @@ public final class WebstrikeElite extends CardImpl {
class WebstrikeEliteTriggeredAbility extends ZoneChangeTriggeredAbility {
WebstrikeEliteTriggeredAbility() {
super(Zone.ALL, null, "", false);
super(Zone.ALL, new DestroyTargetEffect(), "", false);
}
private WebstrikeEliteTriggeredAbility(final WebstrikeEliteTriggeredAbility ability) {

View file

@ -0,0 +1,110 @@
package mage.cards.w;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterPermanentCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.HistoricPredicate;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCardInYourGraveyard;
/**
*
* @author anonymous
*/
public final class WhatMustBeDone extends CardImpl {
private static final FilterPermanent filterArtifactsAndCreatures = new FilterPermanent("artifacts and creatures");
static {
filterArtifactsAndCreatures.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate()
));
}
private static final FilterCard filterHistoricPermanentCard = new FilterPermanentCard("historic permanent card from your graveyard");
static {
filterHistoricPermanentCard.add(HistoricPredicate.instance);
}
public WhatMustBeDone(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}");
// Choose one --
// * Let the World Burn -- Destroy all artifacts and creatures.
this.getSpellAbility().addEffect(new DestroyAllEffect(filterArtifactsAndCreatures));
this.getSpellAbility().withFirstModeFlavorWord("Let the World Burn");
// * Release Juno -- Return target historic permanent card from your graveyard to the battlefield. It enters with two additional +1/+1 counters on it if it's a creature.
this.getSpellAbility().addMode(new Mode(
new WhatMustBeDoneReplacementEffect(
)).addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()
).addTarget(new TargetCardInYourGraveyard(filterHistoricPermanentCard)
).addEffect(new InfoEffect("It enters with two additional +1/+1 counters on it if it's a creature. "
+ "<i>(Artifacts, legendaries, and Sagas are historic.)</i>" )
).withFlavorWord("Release Juno"));
}
private WhatMustBeDone(final WhatMustBeDone card) {
super(card);
}
@Override
public WhatMustBeDone copy() {
return new WhatMustBeDone(this);
}
}
class WhatMustBeDoneReplacementEffect extends ReplacementEffectImpl {
WhatMustBeDoneReplacementEffect() {
super(Duration.EndOfStep, Outcome.BoostCreature);
}
private WhatMustBeDoneReplacementEffect(final WhatMustBeDoneReplacementEffect effect) {
super(effect);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return getTargetPointer().getTargets(game, source).contains(event.getTargetId());
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent == null || !permanent.isCreature(game)) {
return false;
}
permanent.addCounters(CounterType.P1P1.createInstance(2), source.getControllerId(), source, game, event.getAppliedEffects());
discard();
return false;
}
@Override
public WhatMustBeDoneReplacementEffect copy() {
return new WhatMustBeDoneReplacementEffect(this);
}
}

View file

@ -28,8 +28,10 @@ public final class Aetherdrift extends ExpansionSet {
cards.add(new SetCardInfo("Afterburner Expert", 150, Rarity.RARE, mage.cards.a.AfterburnerExpert.class));
cards.add(new SetCardInfo("Agonasaur Rex", 151, Rarity.RARE, mage.cards.a.AgonasaurRex.class));
cards.add(new SetCardInfo("Air Response Unit", 1, Rarity.UNCOMMON, mage.cards.a.AirResponseUnit.class));
cards.add(new SetCardInfo("Alacrian Armory", 2, Rarity.UNCOMMON, mage.cards.a.AlacrianArmory.class));
cards.add(new SetCardInfo("Alacrian Jaguar", 152, Rarity.COMMON, mage.cards.a.AlacrianJaguar.class));
cards.add(new SetCardInfo("Amonkhet Raceway", 248, Rarity.UNCOMMON, mage.cards.a.AmonkhetRaceway.class));
cards.add(new SetCardInfo("Ancient Vendetta", 75, Rarity.UNCOMMON, mage.cards.a.AncientVendetta.class));
cards.add(new SetCardInfo("Apocalypse Runner", 188, Rarity.UNCOMMON, mage.cards.a.ApocalypseRunner.class));
cards.add(new SetCardInfo("Autarch Mammoth", 153, Rarity.UNCOMMON, mage.cards.a.AutarchMammoth.class));
cards.add(new SetCardInfo("Avishkar Raceway", 249, Rarity.COMMON, mage.cards.a.AvishkarRaceway.class));
@ -60,6 +62,10 @@ public final class Aetherdrift extends ExpansionSet {
cards.add(new SetCardInfo("Burnout Bashtronaut", 115, Rarity.RARE, mage.cards.b.BurnoutBashtronaut.class));
cards.add(new SetCardInfo("Caelorna, Coral Tyrant", 40, Rarity.UNCOMMON, mage.cards.c.CaelornaCoralTyrant.class));
cards.add(new SetCardInfo("Camera Launcher", 232, Rarity.COMMON, mage.cards.c.CameraLauncher.class));
cards.add(new SetCardInfo("Captain Howler, Sea Scourge", 194, Rarity.RARE, mage.cards.c.CaptainHowlerSeaScourge.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Captain Howler, Sea Scourge", 361, Rarity.RARE, mage.cards.c.CaptainHowlerSeaScourge.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Captain Howler, Sea Scourge", 475, Rarity.RARE, mage.cards.c.CaptainHowlerSeaScourge.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Canyon Vaulter", 8, Rarity.UNCOMMON, mage.cards.c.CanyonVaulter.class));
cards.add(new SetCardInfo("Caradora, Heart of Alacria", 195, Rarity.RARE, mage.cards.c.CaradoraHeartOfAlacria.class));
cards.add(new SetCardInfo("Carrion Cruiser", 78, Rarity.UNCOMMON, mage.cards.c.CarrionCruiser.class));
cards.add(new SetCardInfo("Chandra, Spark Hunter", 116, Rarity.MYTHIC, mage.cards.c.ChandraSparkHunter.class));
@ -67,16 +73,30 @@ public final class Aetherdrift extends ExpansionSet {
cards.add(new SetCardInfo("Clamorous Ironclad", 117, Rarity.COMMON, mage.cards.c.ClamorousIronclad.class));
cards.add(new SetCardInfo("Cloudspire Captain", 9, Rarity.UNCOMMON, mage.cards.c.CloudspireCaptain.class));
cards.add(new SetCardInfo("Cloudspire Coordinator", 196, Rarity.UNCOMMON, mage.cards.c.CloudspireCoordinator.class));
cards.add(new SetCardInfo("Cloudspire Skycycle", 197, Rarity.UNCOMMON, mage.cards.c.CloudspireSkycycle.class));
cards.add(new SetCardInfo("Coalstoke Gearhulk", 198, Rarity.MYTHIC, mage.cards.c.CoalstokeGearhulk.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Coalstoke Gearhulk", 349, Rarity.MYTHIC, mage.cards.c.CoalstokeGearhulk.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Coalstoke Gearhulk", 477, Rarity.MYTHIC, mage.cards.c.CoalstokeGearhulk.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Coalstoke Gearhulk", 548, Rarity.MYTHIC, mage.cards.c.CoalstokeGearhulk.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Collision Course", 10, Rarity.COMMON, mage.cards.c.CollisionCourse.class));
cards.add(new SetCardInfo("Count on Luck", 118, Rarity.RARE, mage.cards.c.CountOnLuck.class));
cards.add(new SetCardInfo("Country Roads", 253, Rarity.UNCOMMON, mage.cards.c.CountryRoads.class));
cards.add(new SetCardInfo("Crash and Burn", 119, Rarity.COMMON, mage.cards.c.CrashAndBurn.class));
cards.add(new SetCardInfo("Cryptcaller Chariot", 80, Rarity.RARE, mage.cards.c.CryptcallerChariot.class));
cards.add(new SetCardInfo("Cursecloth Wrappings", 81, Rarity.RARE, mage.cards.c.CurseclothWrappings.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Cursecloth Wrappings", 383, Rarity.RARE, mage.cards.c.CurseclothWrappings.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Cursecloth Wrappings", 400, Rarity.MYTHIC, mage.cards.c.CurseclothWrappings.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Cursecloth Wrappings", 410, Rarity.MYTHIC, mage.cards.c.CurseclothWrappings.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Cursecloth Wrappings", 447, Rarity.RARE, mage.cards.c.CurseclothWrappings.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Daretti, Rocketeer Engineer", 120, Rarity.RARE, mage.cards.d.DarettiRocketeerEngineer.class));
cards.add(new SetCardInfo("Daring Mechanic", 11, Rarity.COMMON, mage.cards.d.DaringMechanic.class));
cards.add(new SetCardInfo("Deathless Pilot", 82, Rarity.COMMON, mage.cards.d.DeathlessPilot.class));
cards.add(new SetCardInfo("Debris Beetle", 199, Rarity.RARE, mage.cards.d.DebrisBeetle.class));
cards.add(new SetCardInfo("Defend the Rider", 157, Rarity.UNCOMMON, mage.cards.d.DefendTheRider.class));
cards.add(new SetCardInfo("Demonic Junker", 83, Rarity.RARE, mage.cards.d.DemonicJunker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Demonic Junker", 307, Rarity.RARE, mage.cards.d.DemonicJunker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Demonic Junker", 448, Rarity.RARE, mage.cards.d.DemonicJunker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Demonic Junker", 524, Rarity.RARE, mage.cards.d.DemonicJunker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Detention Chariot", 12, Rarity.UNCOMMON, mage.cards.d.DetentionChariot.class));
cards.add(new SetCardInfo("Dismal Backwater", 254, Rarity.COMMON, mage.cards.d.DismalBackwater.class));
cards.add(new SetCardInfo("District Mascot", 158, Rarity.RARE, mage.cards.d.DistrictMascot.class));
@ -84,6 +104,7 @@ public final class Aetherdrift extends ExpansionSet {
cards.add(new SetCardInfo("Draconautics Engineer", 121, Rarity.RARE, mage.cards.d.DraconauticsEngineer.class));
cards.add(new SetCardInfo("Dracosaur Auxiliary", 122, Rarity.UNCOMMON, mage.cards.d.DracosaurAuxiliary.class));
cards.add(new SetCardInfo("Dredger's Insight", 159, Rarity.UNCOMMON, mage.cards.d.DredgersInsight.class));
cards.add(new SetCardInfo("Dune Drifter", 200, Rarity.UNCOMMON, mage.cards.d.DuneDrifter.class));
cards.add(new SetCardInfo("Dynamite Diver", 123, Rarity.COMMON, mage.cards.d.DynamiteDiver.class));
cards.add(new SetCardInfo("Earthrumbler", 160, Rarity.UNCOMMON, mage.cards.e.Earthrumbler.class));
cards.add(new SetCardInfo("Embalmed Ascendant", 201, Rarity.UNCOMMON, mage.cards.e.EmbalmedAscendant.class));

View file

@ -110,6 +110,8 @@ public final class AetherdriftCommander extends ExpansionSet {
cards.add(new SetCardInfo("Midnight Reaper", 44, Rarity.RARE, mage.cards.m.MidnightReaper.class));
cards.add(new SetCardInfo("Murderous Rider", 45, Rarity.RARE, mage.cards.m.MurderousRider.class));
cards.add(new SetCardInfo("Never // Return", 96, Rarity.RARE, mage.cards.n.NeverReturn.class));
cards.add(new SetCardInfo("Nissa, Worldsoul Speaker", 13, Rarity.RARE, mage.cards.n.NissaWorldsoulSpeaker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Nissa, Worldsoul Speaker", 29, Rarity.RARE, mage.cards.n.NissaWorldsoulSpeaker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("On Wings of Gold", 5, Rarity.RARE, mage.cards.o.OnWingsOfGold.class));
cards.add(new SetCardInfo("One with the Machine", 80, Rarity.RARE, mage.cards.o.OneWithTheMachine.class));
cards.add(new SetCardInfo("Ornithopter of Paradise", 133, Rarity.COMMON, mage.cards.o.OrnithopterOfParadise.class));

View file

@ -30,9 +30,9 @@ public final class AssassinsCreed extends ExpansionSet {
cards.add(new SetCardInfo("Adewale, Breaker of Chains", 44, Rarity.UNCOMMON, mage.cards.a.AdewaleBreakerOfChains.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Adrestia", 252, Rarity.UNCOMMON, mage.cards.a.Adrestia.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Adrestia", 68, Rarity.UNCOMMON, mage.cards.a.Adrestia.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 134, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 214, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 33, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 134, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 214, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Alexios, Deimos of Kosmos", 33, Rarity.UNCOMMON, mage.cards.a.AlexiosDeimosOfKosmos.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Altair Ibn-La'Ahad", 137, Rarity.MYTHIC, mage.cards.a.AltairIbnLaAhad.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Altair Ibn-La'Ahad", 225, Rarity.MYTHIC, mage.cards.a.AltairIbnLaAhad.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Altair Ibn-La'Ahad", 268, Rarity.MYTHIC, mage.cards.a.AltairIbnLaAhad.class, NON_FULL_USE_VARIOUS));
@ -77,8 +77,8 @@ public final class AssassinsCreed extends ExpansionSet {
cards.add(new SetCardInfo("Bleeding Effect", 232, Rarity.UNCOMMON, mage.cards.b.BleedingEffect.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bleeding Effect", 51, Rarity.UNCOMMON, mage.cards.b.BleedingEffect.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Brotherhood Ambushers", 285, Rarity.UNCOMMON, mage.cards.b.BrotherhoodAmbushers.class));
//cards.add(new SetCardInfo("Brotherhood Headquarters", 266, Rarity.UNCOMMON, mage.cards.b.BrotherhoodHeadquarters.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Brotherhood Headquarters", 80, Rarity.UNCOMMON, mage.cards.b.BrotherhoodHeadquarters.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Brotherhood Headquarters", 266, Rarity.UNCOMMON, mage.cards.b.BrotherhoodHeadquarters.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Brotherhood Headquarters", 80, Rarity.UNCOMMON, mage.cards.b.BrotherhoodHeadquarters.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Brotherhood Patriarch", 286, Rarity.COMMON, mage.cards.b.BrotherhoodPatriarch.class));
cards.add(new SetCardInfo("Brotherhood Regalia", 255, Rarity.UNCOMMON, mage.cards.b.BrotherhoodRegalia.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Brotherhood Regalia", 71, Rarity.UNCOMMON, mage.cards.b.BrotherhoodRegalia.class, NON_FULL_USE_VARIOUS));
@ -157,9 +157,9 @@ public final class AssassinsCreed extends ExpansionSet {
//cards.add(new SetCardInfo("Havi, the All-Father", 56, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haystack", 175, Rarity.UNCOMMON, mage.cards.h.Haystack.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haystack", 5, Rarity.UNCOMMON, mage.cards.h.Haystack.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Haytham Kenway", 147, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Haytham Kenway", 238, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Haytham Kenway", 57, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haytham Kenway", 57, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haytham Kenway", 147, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haytham Kenway", 238, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Headsplitter", 289, Rarity.COMMON, mage.cards.h.Headsplitter.class));
cards.add(new SetCardInfo("Hemlock Vial", 206, Rarity.UNCOMMON, mage.cards.h.HemlockVial.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Hemlock Vial", 26, Rarity.UNCOMMON, mage.cards.h.HemlockVial.class, NON_FULL_USE_VARIOUS));
@ -321,12 +321,12 @@ public final class AssassinsCreed extends ExpansionSet {
cards.add(new SetCardInfo("Towering Viewpoint", 263, Rarity.UNCOMMON, mage.cards.t.ToweringViewpoint.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Towering Viewpoint", 77, Rarity.UNCOMMON, mage.cards.t.ToweringViewpoint.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Tranquilize", 284, Rarity.COMMON, mage.cards.t.Tranquilize.class));
//cards.add(new SetCardInfo("Viewpoint Synchronization", 223, Rarity.UNCOMMON, mage.cards.v.ViewpointSynchronization.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Viewpoint Synchronization", 43, Rarity.UNCOMMON, mage.cards.v.ViewpointSynchronization.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Viewpoint Synchronization", 223, Rarity.UNCOMMON, mage.cards.v.ViewpointSynchronization.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Viewpoint Synchronization", 43, Rarity.UNCOMMON, mage.cards.v.ViewpointSynchronization.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Waterlogged Grove", 116, Rarity.RARE, mage.cards.w.WaterloggedGrove.class));
//cards.add(new SetCardInfo("What Must Be Done", 11, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("What Must Be Done", 157, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("What Must Be Done", 184, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("What Must Be Done", 11, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("What Must Be Done", 157, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("What Must Be Done", 184, Rarity.RARE, mage.cards.w.WhatMustBeDone.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Yggdrasil, Rebirth Engine", 126, Rarity.MYTHIC, mage.cards.y.YggdrasilRebirthEngine.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Yggdrasil, Rebirth Engine", 264, Rarity.MYTHIC, mage.cards.y.YggdrasilRebirthEngine.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Yggdrasil, Rebirth Engine", 78, Rarity.MYTHIC, mage.cards.y.YggdrasilRebirthEngine.class, NON_FULL_USE_VARIOUS));

View file

@ -95,10 +95,10 @@ public final class DoctorWho extends ExpansionSet {
cards.add(new SetCardInfo("Bessie, the Doctor's Roadster", 776, Rarity.RARE, mage.cards.b.BessieTheDoctorsRoadster.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bigger on the Inside", 115, Rarity.UNCOMMON, mage.cards.b.BiggerOnTheInside.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bigger on the Inside", 720, Rarity.UNCOMMON, mage.cards.b.BiggerOnTheInside.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Bill Potts", 379, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Bill Potts", 681, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Bill Potts", 76, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Bill Potts", 970, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bill Potts", 379, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bill Potts", 681, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bill Potts", 76, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Bill Potts", 970, Rarity.RARE, mage.cards.b.BillPotts.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Blasphemous Act", 1063, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Blasphemous Act", 224, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Blasphemous Act", 472, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS));
@ -955,8 +955,8 @@ public final class DoctorWho extends ExpansionSet {
cards.add(new SetCardInfo("The Fugitive Doctor", 417, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("The Fugitive Doctor", 541, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("The Fugitive Doctor", 735, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("The Girl in the Fireplace", 21, Rarity.RARE, mage.cards.t.TheGirlInTheFireplace.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("The Girl in the Fireplace", 626, Rarity.RARE, mage.cards.t.TheGirlInTheFireplace.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("The Girl in the Fireplace", 21, Rarity.RARE, mage.cards.t.TheGirlInTheFireplace.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("The Girl in the Fireplace", 626, Rarity.RARE, mage.cards.t.TheGirlInTheFireplace.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("The Lux Foundation Library", 588, Rarity.COMMON, mage.cards.t.TheLuxFoundationLibrary.class));
//cards.add(new SetCardInfo("The Master, Formed Anew", 1017, Rarity.RARE, mage.cards.t.TheMasterFormedAnew.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("The Master, Formed Anew", 1133, Rarity.RARE, mage.cards.t.TheMasterFormedAnew.class, NON_FULL_USE_VARIOUS));

View file

@ -835,10 +835,10 @@ public final class Fallout extends ExpansionSet {
//cards.add(new SetCardInfo("Strong Back", 611, Rarity.RARE, mage.cards.s.StrongBack.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong Back", 83, Rarity.RARE, mage.cards.s.StrongBack.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong Back", 930, Rarity.RARE, mage.cards.s.StrongBack.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong, the Brutish Thespian", 403, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong, the Brutish Thespian", 612, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong, the Brutish Thespian", 84, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Strong, the Brutish Thespian", 931, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Strong, the Brutish Thespian", 403, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Strong, the Brutish Thespian", 612, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Strong, the Brutish Thespian", 84, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Strong, the Brutish Thespian", 931, Rarity.RARE, mage.cards.s.StrongTheBrutishThespian.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Struggle for Project Purity", 380, Rarity.RARE, mage.cards.s.StruggleForProjectPurity.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Struggle for Project Purity", 39, Rarity.RARE, mage.cards.s.StruggleForProjectPurity.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Struggle for Project Purity", 567, Rarity.RARE, mage.cards.s.StruggleForProjectPurity.class, NON_FULL_USE_VARIOUS));

View file

@ -0,0 +1,28 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
* @author TheElk801
*/
public final class FinalFantasy extends ExpansionSet {
private static final FinalFantasy instance = new FinalFantasy();
public static FinalFantasy getInstance() {
return instance;
}
private FinalFantasy() {
super("Final Fantasy", "FIN", ExpansionSet.buildDate(2025, 6, 13), SetType.EXPANSION);
this.blockName = "Final Fantasy"; // for sorting in GUI
this.hasBasicLands = false; // temporary
cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class));
cards.add(new SetCardInfo("Sazh's Chocobo", 200, Rarity.UNCOMMON, mage.cards.s.SazhsChocobo.class));
cards.add(new SetCardInfo("Summon: Shiva", 78, Rarity.UNCOMMON, mage.cards.s.SummonShiva.class));
cards.add(new SetCardInfo("Tonberry", 122, Rarity.UNCOMMON, mage.cards.t.Tonberry.class));
}
}

View file

@ -0,0 +1,24 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
* @author TheElk801
*/
public final class FinalFantasyCommander extends ExpansionSet {
private static final FinalFantasyCommander instance = new FinalFantasyCommander();
public static FinalFantasyCommander getInstance() {
return instance;
}
private FinalFantasyCommander() {
super("Final Fantasy Commander", "FIC", ExpansionSet.buildDate(2025, 6, 13), SetType.SUPPLEMENTAL);
this.hasBasicLands = false;
cards.add(new SetCardInfo("Terra, Herald of Hope", 4, Rarity.MYTHIC, mage.cards.t.TerraHeraldOfHope.class));
}
}

View file

@ -253,7 +253,7 @@ public final class RavnicaClueEdition extends ExpansionSet {
cards.add(new SetCardInfo("Stunt Double", 101, Rarity.RARE, mage.cards.s.StuntDouble.class));
cards.add(new SetCardInfo("Sumala Rumblers", 45, Rarity.UNCOMMON, mage.cards.s.SumalaRumblers.class));
cards.add(new SetCardInfo("Sunhome Stalwart", 73, Rarity.UNCOMMON, mage.cards.s.SunhomeStalwart.class));
//cards.add(new SetCardInfo("Suppressor Skyguard", 46, Rarity.RARE, mage.cards.s.SuppressorSkyguard.class));
cards.add(new SetCardInfo("Suppressor Skyguard", 46, Rarity.RARE, mage.cards.s.SuppressorSkyguard.class));
cards.add(new SetCardInfo("Supreme Verdict", 211, Rarity.RARE, mage.cards.s.SupremeVerdict.class));
cards.add(new SetCardInfo("Swamp", 262, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Swamp", 263, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));

View file

@ -0,0 +1,32 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
* @author TheElk801
*/
public final class TarkirDragonstorm extends ExpansionSet {
private static final TarkirDragonstorm instance = new TarkirDragonstorm();
public static TarkirDragonstorm getInstance() {
return instance;
}
private TarkirDragonstorm() {
super("Tarkir: Dragonstorm", "TDM", ExpansionSet.buildDate(2025, 4, 11), SetType.EXPANSION);
this.blockName = "Tarkir: Dragonstorm"; // for sorting in GUI
this.hasBasicLands = false; // temporary
cards.add(new SetCardInfo("Barrensteppe Siege", 171, Rarity.RARE, mage.cards.b.BarrensteppeSiege.class));
cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class));
cards.add(new SetCardInfo("Mox Jasper", 246, Rarity.MYTHIC, mage.cards.m.MoxJasper.class));
cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 209, Rarity.RARE, mage.cards.n.NarsetJeskaiWaymaster.class));
cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class));
cards.add(new SetCardInfo("Skirmish Rhino", 224, Rarity.UNCOMMON, mage.cards.s.SkirmishRhino.class));
cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class));
cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class));
}
}

View file

@ -0,0 +1,24 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
* @author TheElk801
*/
public final class TarkirDragonstormCommander extends ExpansionSet {
private static final TarkirDragonstormCommander instance = new TarkirDragonstormCommander();
public static TarkirDragonstormCommander getInstance() {
return instance;
}
private TarkirDragonstormCommander() {
super("Tarkir: Dragonstorm Commander", "TDC", ExpansionSet.buildDate(2025, 4, 11), SetType.SUPPLEMENTAL);
this.hasBasicLands = false;
cards.add(new SetCardInfo("Teval, the Balanced Scale", 8, Rarity.MYTHIC, mage.cards.t.TevalTheBalancedScale.class));
}
}

View file

@ -0,0 +1,63 @@
package org.mage.test.cards.single.dft;
import mage.abilities.keyword.FlyingAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author jimga150
*/
public class MuYanlingWindRiderTest extends CardTestPlayerBase {
// When this creature enters, create a 3/2 colorless Vehicle artifact token with crew 1.
// Vehicles you control have flying.
// Whenever one or more creatures you control with flying deal combat damage to a player, draw a card.
private final String muyanling = "Mu Yanling, Wind Rider";
@Test
public void testToken() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, muyanling);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, muyanling, true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertPermanentCount(playerA, muyanling, 1);
assertPermanentCount(playerA, "Vehicle Token", 1);
assertAbility(playerA, "Vehicle Token", FlyingAbility.getInstance(), true);
}
@Test
public void testDraw() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.HAND, playerA, muyanling);
addCard(Zone.HAND, playerA, "Memnite");
addCard(Zone.BATTLEFIELD, playerA, "Ankle Biter");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, muyanling, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite", true);
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1");
setChoice(playerA, "Memnite");
attack(3, playerA, "Vehicle Token", playerB);
attack(3, playerA, muyanling, playerB);
attack(3, playerA, "Ankle Biter", playerB);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertPermanentCount(playerA, muyanling, 1);
assertPermanentCount(playerA, "Vehicle Token", 1);
assertAbility(playerA, "Vehicle Token", FlyingAbility.getInstance(), true);
assertHandCount(playerA, 2); // 1 for turn plus 1 for attack
}
}

View file

@ -0,0 +1,118 @@
package org.mage.test.cards.single.ncc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class RainOfRichesTest extends CardTestPlayerBase {
/**
* {@link mage.cards.r.RainOfRiches Rain of Riches} {3}{R}{R}
* Enchantment
* When Rain of Riches enters the battlefield, create two Treasure tokens.
* The first spell you cast each turn that mana from a Treasure was spent to cast has cascade.
*/
private static final String rain = "Rain of Riches";
@Test
public void test_Using_Treasures() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, rain, 1);
addCard(Zone.HAND, playerA, "Goblin Piker", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.LIBRARY, playerA, "Elite Vanguard", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rain, true);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Goblin Piker");
setChoice(playerA, "Red"); // choice for treasure mana
setChoice(playerA, "Red"); // choice for treasure mana
setChoice(playerA, true); // yes to Cascade
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Goblin Piker", 1);
assertPermanentCount(playerA, "Elite Vanguard", 1);
}
@Test
public void test_Not_Using_Treasures() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, rain, 1);
addCard(Zone.HAND, playerA, "Goblin Piker", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7);
addCard(Zone.LIBRARY, playerA, "Elite Vanguard", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rain, true);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Goblin Piker");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Goblin Piker", 1);
assertPermanentCount(playerA, "Elite Vanguard", 0);
}
@Test
@Ignore("Does not work until actions are processed between the last mana being paid and the spell being cast.")
public void test_Cast_Two_Using_Treasures() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.HAND, playerA, rain, 1);
addCard(Zone.HAND, playerA, "Raging Goblin", 2); // {R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.LIBRARY, playerA, "Memnite", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rain, true);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Raging Goblin");
setChoice(playerA, "Red"); // choice for treasure mana
setChoice(playerA, true); // yes to Cascade
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Raging Goblin");
setChoice(playerA, "Red"); // choice for treasure mana
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Raging Goblin", 2);
assertPermanentCount(playerA, "Memnite", 1);
}
@Test
@Ignore("Does not work until actions are processed between the last mana being paid and the spell being cast.")
public void test_Cast_SomethingElse_Then_Cast_Using_Treasure() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, rain, 1);
addCard(Zone.HAND, playerA, "Raging Goblin", 2); // {R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.LIBRARY, playerA, "Memnite", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rain, true);
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Raging Goblin", true);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Raging Goblin");
setChoice(playerA, "Red"); // choice for treasure mana
setChoice(playerA, true); // yes to Cascade
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Raging Goblin", 2);
assertPermanentCount(playerA, "Memnite", 1);
}
}

View file

@ -1,13 +1,11 @@
package org.mage.test.cards.single.vis;
import mage.game.permanent.Permanent;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static mage.constants.PhaseStep.*;
import static mage.constants.Zone.BATTLEFIELD;
import static mage.constants.Zone.HAND;
import static org.junit.Assert.assertEquals;
public class BroodOfCockroachesTest extends CardTestPlayerBase {
@ -16,19 +14,6 @@ public class BroodOfCockroachesTest extends CardTestPlayerBase {
private static final String BROOD_OF_COCKROACHES = "Brood of Cockroaches";
private static final String SHOCK = "Shock";
@Test
public void should_display_correct_text() {
String expectedText = "When {this} is put into your graveyard from the battlefield, at the beginning of the next end step, you lose 1 life and return {this} to your hand.";
playerA_casts_Brood_of_Cockroaches_at_precombat_main_phase();
setStopAt(TURN_1, END_TURN);
execute();
Permanent permanent = getPermanent(BROOD_OF_COCKROACHES, playerA);
assertEquals(expectedText, permanent.getAbilities().get(1).toString());
}
@Test
public void should_reduce_life_of_playerA_by_1_at_the_beginning_of_the_next_end_step() {
setLife(playerA, ANY_LIFE_TOTAL);

View file

@ -7,7 +7,10 @@ import mage.abilities.dynamicvalue.common.ControllerSpeedCount;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.constants.*;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
@ -22,7 +25,7 @@ public class MaxSpeedAbility extends StaticAbility {
}
public MaxSpeedAbility(Ability ability) {
super(Zone.ALL, new MaxSpeedAbilityEffect(ability));
super(ability.getZone(), new MaxSpeedAbilityEffect(ability));
}
private MaxSpeedAbility(final MaxSpeedAbility ability) {

View file

@ -0,0 +1,76 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.game.Game;
import mage.watchers.common.SpellsCastWatcher;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
public enum InstantAndSorceryCastThisTurn implements DynamicValue
{
YOU("you've cast"),
ALL("all players have cast"),
OPPONENTS("your opponents have cast");
private final String message;
private final ValueHint hint;
InstantAndSorceryCastThisTurn(String message) {
this.message = "Instant and sorcery spells " + message + " this turn";
this.hint = new ValueHint(this.message, this);
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return getSpellsCastThisTurn(game, sourceAbility);
}
@Override
public InstantAndSorceryCastThisTurn copy() {
return this;
}
@Override
public String getMessage() {
return this.message;
}
public Hint getHint() {
return this.hint;
}
private int getSpellsCastThisTurn(Game game, Ability ability) {
Collection<UUID> playerIds;
switch (this) {
case YOU:
playerIds = Collections.singletonList(ability.getControllerId());
break;
case ALL:
playerIds = game.getState().getPlayersInRange(ability.getControllerId(), game);
break;
case OPPONENTS:
playerIds = game.getOpponents(ability.getControllerId());
break;
default:
return 0;
}
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
if (watcher == null) {
return 0;
}
return (int) playerIds.stream()
.map(watcher::getSpellsCastThisTurn)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.filter(spell -> spell.isInstantOrSorcery(game))
.count();
}
}

View file

@ -22,7 +22,6 @@ import mage.game.stack.Spell;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
import mage.util.trace.TraceInfo;
import org.apache.log4j.Logger;
import java.io.Serializable;
@ -1405,88 +1404,6 @@ public class ContinuousEffects implements Serializable {
return controllerFound;
}
/**
* Debug only: prints out a status of the currently existing continuous effects
*
* @param game
*/
public void traceContinuousEffects(Game game) {
game.getContinuousEffects().getLayeredEffects(game);
logger.info("-------------------------------------------------------------------------------------------------");
int numberEffects = 0;
for (ContinuousEffectsList list : allEffectsLists) {
numberEffects += list.size();
}
logger.info("Turn: " + game.getTurnNum() + " - currently existing continuous effects: " + numberEffects);
logger.info("layeredEffects ...................: " + layeredEffects.size());
logger.info("continuousRuleModifyingEffects ...: " + continuousRuleModifyingEffects.size());
logger.info("replacementEffects ...............: " + replacementEffects.size());
logger.info("preventionEffects ................: " + preventionEffects.size());
logger.info("requirementEffects ...............: " + requirementEffects.size());
logger.info("restrictionEffects ...............: " + restrictionEffects.size());
logger.info("restrictionUntapNotMoreThanEffects: " + restrictionUntapNotMoreThanEffects.size());
logger.info("costModificationEffects ..........: " + costModificationEffects.size());
logger.info("spliceCardEffects ................: " + spliceCardEffects.size());
logger.info("asThoughEffects:");
for (Map.Entry<AsThoughEffectType, ContinuousEffectsList<AsThoughEffect>> entry : asThoughEffectsMap.entrySet()) {
logger.info("... " + entry.getKey().toString() + ": " + entry.getValue().size());
}
logger.info("applyStatus ....................: " + (applyStatus != null ? "exists" : "null"));
logger.info("auraReplacementEffect ............: " + (continuousRuleModifyingEffects != null ? "exists" : "null"));
Map<String, TraceInfo> orderedEffects = new TreeMap<>();
traceAddContinuousEffects(orderedEffects, layeredEffects, game, "layeredEffects................");
traceAddContinuousEffects(orderedEffects, continuousRuleModifyingEffects, game, "continuousRuleModifyingEffects");
traceAddContinuousEffects(orderedEffects, replacementEffects, game, "replacementEffects............");
traceAddContinuousEffects(orderedEffects, preventionEffects, game, "preventionEffects.............");
traceAddContinuousEffects(orderedEffects, requirementEffects, game, "requirementEffects............");
traceAddContinuousEffects(orderedEffects, restrictionEffects, game, "restrictionEffects............");
traceAddContinuousEffects(orderedEffects, restrictionUntapNotMoreThanEffects, game, "restrictionUntapNotMore...");
traceAddContinuousEffects(orderedEffects, costModificationEffects, game, "costModificationEffects.......");
traceAddContinuousEffects(orderedEffects, spliceCardEffects, game, "spliceCardEffects.............");
for (Map.Entry<AsThoughEffectType, ContinuousEffectsList<AsThoughEffect>> entry : asThoughEffectsMap.entrySet()) {
traceAddContinuousEffects(orderedEffects, entry.getValue(), game, entry.getKey().toString());
}
String playerName = "";
for (Map.Entry<String, TraceInfo> entry : orderedEffects.entrySet()) {
if (!entry.getValue().getPlayerName().equals(playerName)) {
playerName = entry.getValue().getPlayerName();
logger.info("--- Player: " + playerName + " --------------------------------");
}
logger.info(entry.getValue().getInfo()
+ " " + entry.getValue().getSourceName()
+ " " + entry.getValue().getDuration().name()
+ " " + entry.getValue().getRule()
+ " (Order: " + entry.getValue().getOrder() + ")"
);
}
logger.info("---- End trace Continuous effects --------------------------------------------------------------------------");
}
public static void traceAddContinuousEffects(Map orderedEffects, ContinuousEffectsList<?> cel, Game game, String listName) {
for (ContinuousEffect effect : cel) {
Set<Ability> abilities = cel.getAbility(effect.getId());
for (Ability ability : abilities) {
Player controller = game.getPlayer(ability.getControllerId());
MageObject source = game.getObject(ability.getSourceId());
TraceInfo traceInfo = new TraceInfo();
traceInfo.setInfo(listName);
traceInfo.setOrder(effect.getOrder());
if (ability instanceof MageSingleton) {
traceInfo.setPlayerName("Mage Singleton");
traceInfo.setSourceName("Mage Singleton");
} else {
traceInfo.setPlayerName(controller == null ? "no controller" : controller.getName());
traceInfo.setSourceName(source == null ? "no source" : source.getIdName());
}
traceInfo.setRule(ability.getRule());
traceInfo.setAbilityId(ability.getId());
traceInfo.setEffectId(effect.getId());
traceInfo.setDuration(effect.getDuration());
orderedEffects.put(traceInfo.getPlayerName() + traceInfo.getSourceName() + effect.getId() + ability.getId(), traceInfo);
}
}
}
public int getTotalEffectsCount() {
return allEffectsLists.stream().mapToInt(ContinuousEffectsList::size).sum();
}

View file

@ -7,6 +7,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.game.Game;
import mage.target.targetpointer.TargetPointer;
import mage.util.CardUtil;
/**
* @author BetaSteward_at_googlemail.com
@ -67,7 +68,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
return staticText;
}
if (ability.getRuleVisible()) {
return rulePrefix + ability.getRule();
return rulePrefix + CardUtil.getTextWithFirstCharLowerCase(ability.getRule());
} else {
return "";
}

View file

@ -10,14 +10,26 @@ import mage.target.targetpointer.FixedTarget;
public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl {
private final boolean mountsAlso;
private final boolean yourMainPhaseOnly;
public CrewsVehicleSourceTriggeredAbility(Effect effect) {
this(effect, false, false);
}
public CrewsVehicleSourceTriggeredAbility(Effect effect, boolean mountsAlso, boolean yourMainPhaseOnly) {
super(Zone.BATTLEFIELD, effect, false);
this.addIcon(CardIconImpl.ABILITY_CREW);
setTriggerPhrase("Whenever {this} crews a Vehicle, ");
this.mountsAlso = mountsAlso;
this.yourMainPhaseOnly = yourMainPhaseOnly;
setTriggerPhrase("Whenever {this}" + (mountsAlso ? " saddles a Mount or" : "") +
" crews a Vehicle" + (yourMainPhaseOnly ? " during your main phase" : "") + ", ");
}
protected CrewsVehicleSourceTriggeredAbility(final CrewsVehicleSourceTriggeredAbility ability) {
super(ability);
this.mountsAlso = ability.mountsAlso;
this.yourMainPhaseOnly = ability.yourMainPhaseOnly;
}
@Override
@ -27,11 +39,14 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CREWED_VEHICLE;
return event.getType() == GameEvent.EventType.CREWED_VEHICLE || (mountsAlso && event.getType() == GameEvent.EventType.SADDLED_MOUNT);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (yourMainPhaseOnly && !(game.isMainPhase() && this.isControlledBy(game.getActivePlayerId()))) {
return false;
}
if (event.getTargetId().equals(getSourceId())) {
for (Effect effect : getEffects()) {
// set the vehicle id as target

View file

@ -45,22 +45,22 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
}
for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) {
if (filter.match(card, game)) {
if (filter.match(card, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}
}
for (Card card : player.getLibrary().getCards(game)) {
if (filter.match(card, game)) {
if (filter.match(card, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}
}
for (Card card : player.getHand().getCards(game)) {
if (filter.match(card, game)) {
if (filter.match(card, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}
}
for (Card card : player.getGraveyard().getCards(game)) {
if (filter.match(card, game)) {
if (filter.match(card, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}
}
@ -68,7 +68,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY)
.stream()
.filter(card -> filter.match(card, game))
.filter(card -> filter.match(card, player.getId(), source, game))
.forEach(card -> game.getState().addOtherAbility(card, ability));
for (StackObject stackObject : game.getStack()) {
@ -77,7 +77,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
}
// TODO: Distinguish "you cast" to exclude copies
Card card = game.getCard(stackObject.getSourceId());
if (card != null && filter.match((Spell) stackObject, game)) {
if (card != null && filter.match((Spell) stackObject, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}
}

View file

@ -12,27 +12,37 @@ import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInLibrary;
import mage.util.CardUtil;
/**
* @author BetaSteward_at_googlemail.com, edited by Cguy7777
*/
public class SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect extends SearchEffect {
public class SearchLibraryPutOntoBattlefieldTappedRestInHandEffect extends SearchEffect {
private static final FilterCard filter = new FilterCard("card to put on the battlefield tapped");
private final FilterCard filter;
private final int numToBattlefield;
public SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target) {
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target, int numToBattlefield) {
super(target, Outcome.PutLandInPlay);
staticText = "search your library for " + target.getDescription() +
", reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle";
", reveal those cards, put " + CardUtil.numberToText(numToBattlefield) + " onto the battlefield tapped and the other into your hand, then shuffle";
this.filter = new FilterCard((numToBattlefield > 1 ? "cards" : "card") + "to put on the battlefield tapped");
this.numToBattlefield = numToBattlefield;
}
protected SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(final SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect effect) {
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target) {
this(target, 1);
}
protected SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(final SearchLibraryPutOntoBattlefieldTappedRestInHandEffect effect) {
super(effect);
this.filter = effect.filter.copy();
this.numToBattlefield = effect.numToBattlefield;
}
@Override
public SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect copy() {
return new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(this);
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect copy() {
return new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(this);
}
@Override
@ -49,14 +59,15 @@ public class SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect extends Se
controller.revealCards(sourceObject.getIdName(), revealed, game);
if (target.getTargets().size() >= 2) {
TargetCardInLibrary targetCardToBattlefield = new TargetCardInLibrary(filter);
controller.choose(Outcome.PutLandInPlay, revealed, targetCardToBattlefield, source, game);
int maxToBattlefield = Math.min(numToBattlefield, target.getTargets().size());
TargetCardInLibrary targetCardsToBattlefield = new TargetCardInLibrary(maxToBattlefield, filter);
controller.choose(Outcome.PutLandInPlay, revealed, targetCardsToBattlefield, source, game);
Card cardToBattlefield = revealed.get(targetCardToBattlefield.getFirstTarget(), game);
Cards cardsToBattlefield = new CardsImpl(targetCardsToBattlefield.getTargets());
Cards cardsToHand = new CardsImpl(revealed);
if (cardToBattlefield != null) {
controller.moveCards(cardToBattlefield, Zone.BATTLEFIELD, source, game, true, false, false, null);
cardsToHand.remove(cardToBattlefield);
if (!cardsToBattlefield.isEmpty()) {
controller.moveCards(cardsToBattlefield.getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null);
cardsToHand.removeAll(cardsToBattlefield);
}
controller.moveCardsToHandWithInfo(cardsToHand, source, game, true);

View file

@ -551,7 +551,7 @@ public enum SubType {
@Override
public String toString() {
return "Subtype(" + subtype + ')';
return "Subtype(" + subtype + ')'; // warning, do not change until refactor code like predicate.toString().equals
}
}

View file

@ -3814,7 +3814,7 @@ public abstract class GameImpl implements Game {
@Override
public boolean endTurn(Ability source) {
getTurn().endTurn(this, getActivePlayerId(), source);
getTurn().endTurn(this, source);
return true;
}

View file

@ -33,7 +33,6 @@ import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetDefender;
import mage.util.CardUtil;
import mage.util.Copyable;
import mage.util.trace.TraceUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
@ -744,8 +743,6 @@ public class Combat implements Serializable, Copyable<Combat> {
game.getCombat().logBlockerInfo(defender, game);
}
}
// tool to catch the bug about flyers blocked by non flyers or intimidate blocked by creatures with other colors
TraceUtil.traceCombatIfNeeded(game, game.getCombat());
}
private void makeSureItsNotComputer(Player controller) {
@ -761,7 +758,7 @@ public class Combat implements Serializable, Copyable<Combat> {
* Add info about attacker blocked by blocker to the game log
*/
private void logBlockerInfo(Player defender, Game game) {
boolean shownDefendingPlayer = game.getPlayers().size() < 3; // only two players no need to saw the attacked player
boolean shownDefendingPlayer = game.getPlayers().size() <= 2; // 1 vs 1 game, no need to saw the attacked player
for (CombatGroup group : game.getCombat().getGroups()) {
if (group.defendingPlayerId.equals(defender.getId())) {
if (!shownDefendingPlayer) {

View file

@ -70,7 +70,8 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
SpellAbility spellAbility = spell.getSpellAbility();
for (Mode mode : spellAbility.getModes().values()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
for (Target target : mode.getTargets()) {
if (!target.isNotTarget()) {
for (UUID targetId : target.getTargets()) {

View file

@ -16,6 +16,7 @@ import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.command.Emblem;
import mage.game.events.GameEvent;
import mage.players.Player;
/**
@ -106,8 +107,13 @@ class RadiationEffect extends OneShotEffect {
Cards milled = player.millCards(amount, source, game);
int countNonLand = milled.count(StaticFilters.FILTER_CARD_NON_LAND, player.getId(), source, game);
if (countNonLand > 0) {
// TODO: support gaining life instead with [[Strong, the Brutish Thespian]]
player.loseLife(countNonLand, game, source, false);
GameEvent event = new GameEvent(GameEvent.EventType.RADIATION_GAIN_LIFE, null, source, player.getId(), amount, false);
if (game.replaceEvent(event)) {
player.gainLife(countNonLand, game, source);
} else {
player.loseLife(countNonLand, game, source, false);
}
player.loseCounters(CounterType.RAD.getName(), countNonLand, source, game);
}
return true;

Some files were not shown because too many files have changed in this diff Show more