PlayTheTopCard improves: added correct usage check, fixed outdated rule texts, Bolas's Citadel simplified (related to #7605);

This commit is contained in:
Oleg Agafonov 2021-02-21 03:53:33 +04:00
parent bfe91ad32b
commit 48e9cc3e07
27 changed files with 182 additions and 182 deletions

View file

@ -11,16 +11,16 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.cards.*; import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetControlledPermanent;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@ -89,69 +89,37 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl {
@Override @Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return applies(objectId, null, source, game, affectedControllerId); // current card's part
} Card cardToCheck = game.getCard(objectId);
if (cardToCheck == null) {
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
if (!Objects.equals(source.getControllerId(), playerId)) {
return false; return false;
} }
Player player = game.getPlayer(playerId); // must be you
if (player != null) { if (!affectedControllerId.equals(source.getControllerId())) {
Card topCard = player.getLibrary().getFromTop(game); return false;
UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // for adventure cards
if (topCard == null || !topCard.getId().equals(objectIdToCast)) {
return false;
}
if (topCard instanceof SplitCard || topCard instanceof ModalDoubleFacesCard) {
// double faces cards
Card card1;
Card card2;
if (topCard instanceof SplitCard) {
card1 = ((SplitCard) topCard).getLeftHalfCard();
card2 = ((SplitCard) topCard).getRightHalfCard();
} else {
card1 = ((ModalDoubleFacesCard) topCard).getLeftHalfCard();
card2 = ((ModalDoubleFacesCard) topCard).getRightHalfCard();
}
// left
if (!card1.isLand()) {
PayLifeCost lifeCost = new PayLifeCost(card1.getSpellAbility().getManaCosts().convertedManaCost());
Costs newCosts = new CostsImpl();
newCosts.add(lifeCost);
newCosts.addAll(card1.getSpellAbility().getCosts());
player.setCastSourceIdWithAlternateMana(card1.getId(), null, newCosts);
}
// right
if (!card2.isLand()) {
PayLifeCost lifeCost = new PayLifeCost(card2.getSpellAbility().getManaCosts().convertedManaCost());
Costs newCosts = new CostsImpl();
newCosts.add(lifeCost);
newCosts.addAll(card2.getSpellAbility().getCosts());
player.setCastSourceIdWithAlternateMana(card2.getId(), null, newCosts);
}
} else {
// other single face cards
if (!topCard.isLand()) {
if (affectedAbility == null) {
affectedAbility = topCard.getSpellAbility();
} else {
objectIdToCast = affectedAbility.getSourceId();
}
PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost());
Costs costs = new CostsImpl();
costs.add(cost);
costs.addAll(affectedAbility.getCosts());
player.setCastSourceIdWithAlternateMana(objectIdToCast, null, costs);
}
}
return true;
} }
return false;
// must be your card
Player player = game.getPlayer(cardToCheck.getOwnerId());
if (player == null) {
return false;
}
// must be from your library
Card topCard = player.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
// allows to play/cast with alternative life cost
if (!cardToCheck.isLand()) {
PayLifeCost lifeCost = new PayLifeCost(cardToCheck.getSpellAbility().getManaCosts().convertedManaCost());
Costs newCosts = new CostsImpl();
newCosts.add(lifeCost);
newCosts.addAll(cardToCheck.getSpellAbility().getCosts());
player.setCastSourceIdWithAlternateMana(cardToCheck.getId(), null, newCosts);
}
return true;
} }
} }

View file

@ -14,7 +14,6 @@ import mage.filter.FilterCard;
import java.util.UUID; import java.util.UUID;
/** /**
*
* @author htrajan * @author htrajan
*/ */
public final class ConspicuousSnoop extends CardImpl { public final class ConspicuousSnoop extends CardImpl {
@ -37,7 +36,7 @@ public final class ConspicuousSnoop extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may cast Goblin spells from the top of your library. // You may cast Goblin spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
// As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card. // As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.
this.addAbility(new SimpleStaticAbility(new GainActivatedAbilitiesOfTopCardEffect(filter.copy().withMessage("a Goblin card")))); this.addAbility(new SimpleStaticAbility(new GainActivatedAbilitiesOfTopCardEffect(filter.copy().withMessage("a Goblin card"))));

View file

@ -21,7 +21,7 @@ import java.util.UUID;
*/ */
public final class CourserOfKruphix extends CardImpl { public final class CourserOfKruphix extends CardImpl {
private static final FilterCard filter = new FilterLandCard("play land cards"); private static final FilterCard filter = new FilterLandCard("play lands");
public CourserOfKruphix(UUID ownerId, CardSetInfo setInfo) { public CourserOfKruphix(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}{G}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}{G}");
@ -33,8 +33,8 @@ public final class CourserOfKruphix extends CardImpl {
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may play the top card of your library if it's a land card. // You may play lands from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
// Whenever a land enters the battlefield under your control, you gain 1 life. // Whenever a land enters the battlefield under your control, you gain 1 life.
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), StaticFilters.FILTER_LAND_A)); this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), StaticFilters.FILTER_LAND_A));

View file

@ -17,7 +17,7 @@ public final class CrucibleOfWorlds extends CardImpl {
public CrucibleOfWorlds(UUID ownerId, CardSetInfo setInfo) { public CrucibleOfWorlds(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// You may play land cards from your graveyard. // You may play lands from your graveyard.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect()));
} }

View file

@ -45,8 +45,8 @@ public final class ElshaOfTheInfinite extends CardImpl {
// You may look at the top card of your library any time. // You may look at the top card of your library any time.
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast the top card of your library if it's a noncreature, nonland card, and you may cast it as though it had flash. // You may cast noncreature spells from the top of your library. If you cast a spell this way, you may cast it as though it had flash.
Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter)); Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false));
ability.addEffect(new CastAsThoughItHadFlashAllEffect( ability.addEffect(new CastAsThoughItHadFlashAllEffect(
Duration.WhileOnBattlefield, filter Duration.WhileOnBattlefield, filter
).setText("If you cast a spell this way, you may cast it as though it had flash.")); ).setText("If you cast a spell this way, you may cast it as though it had flash."));

View file

@ -30,7 +30,7 @@ public final class ExperimentalFrenzy extends CardImpl {
// You may look at the top card of your library any time. // You may look at the top card of your library any time.
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may play the top card of your library. // You may play lands and cast spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect())); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect()));
// You can't play cards from your hand. // You can't play cards from your hand.

View file

@ -1,7 +1,5 @@
package mage.cards.f; package mage.cards.f;
import java.util.UUID;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect; import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect;
@ -10,18 +8,20 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.constants.Zone;
import java.util.UUID;
/** /**
*
* @author Plopman * @author Plopman
*/ */
public final class FutureSight extends CardImpl { public final class FutureSight extends CardImpl {
public FutureSight(UUID ownerId, CardSetInfo setInfo) { public FutureSight(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}");
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect()));
// You may play the top card of your library.
// You may play lands and cast spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect()));
} }

View file

@ -34,8 +34,8 @@ public final class GarruksHorde extends CardImpl {
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may cast the top card of your library if it's a creature card. // You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
} }
private GarruksHorde(final GarruksHorde card) { private GarruksHorde(final GarruksHorde card) {

View file

@ -1,7 +1,5 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
@ -12,14 +10,15 @@ import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import java.util.UUID;
/** /**
*
* @author emerald000 * @author emerald000
*/ */
public final class MagusOfTheFuture extends CardImpl { public final class MagusOfTheFuture extends CardImpl {
public MagusOfTheFuture(UUID ownerId, CardSetInfo setInfo) { public MagusOfTheFuture(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{U}");
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.WIZARD);
@ -29,7 +28,7 @@ public final class MagusOfTheFuture extends CardImpl {
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect()));
// You may play the top card of your library. // You may play lands and cast spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect()));
} }

View file

@ -17,7 +17,6 @@ import mage.filter.FilterCard;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
@ -49,8 +48,8 @@ public final class MelekIzzetParagon extends CardImpl {
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect()));
// You may cast the top card of your library if it's an instant or sorcery card. // You may cast instant and sorcery spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false)));
// Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy. // Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy.
this.addAbility(new MelekIzzetParagonTriggeredAbility()); this.addAbility(new MelekIzzetParagonTriggeredAbility());

View file

@ -1,9 +1,6 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -14,9 +11,10 @@ import mage.constants.TargetController;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.filter.common.FilterLandPermanent; import mage.filter.common.FilterLandPermanent;
import java.util.UUID;
/** /**
*
* @author North * @author North
*/ */
public final class Mutilate extends CardImpl { public final class Mutilate extends CardImpl {
@ -31,14 +29,15 @@ public final class Mutilate extends CardImpl {
} }
public Mutilate(UUID ownerId, CardSetInfo setInfo) { public Mutilate(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}");
// All creatures get -1/-1 until end of turn for each Swamp you control. // All creatures get -1/-1 until end of turn for each Swamp you control.
PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter, -1); PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter, -1);
ContinuousEffect effect = new BoostAllEffect(count, count, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES, false, null, true); this.getSpellAbility().addEffect(
effect.overrideRuleText(ruleText); new BoostAllEffect(count, count, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES, false, null, true)
this.getSpellAbility().addEffect(effect); .setText(ruleText)
);
} }
private Mutilate(final Mutilate card) { private Mutilate(final Mutilate card) {

View file

@ -43,7 +43,7 @@ public final class MysticForge extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast artifact spells and colorless spells from the top of your library. // You may cast artifact spells and colorless spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
// {T}, Pay 1 life: Exile the top card of your library. // {T}, Pay 1 life: Exile the top card of your library.
Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost()); Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost());

View file

@ -20,7 +20,7 @@ import java.util.UUID;
*/ */
public final class OracleOfMulDaya extends CardImpl { public final class OracleOfMulDaya extends CardImpl {
private static final FilterCard filter = new FilterLandCard("play land cards"); private static final FilterCard filter = new FilterLandCard("play lands");
public OracleOfMulDaya(UUID ownerId, CardSetInfo setInfo) { public OracleOfMulDaya(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
@ -38,8 +38,8 @@ public final class OracleOfMulDaya extends CardImpl {
// Play with the top card of your library revealed. // Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect())); this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may play the top card of your library if it's a land card. // You may play lands from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
} }
private OracleOfMulDaya(final OracleOfMulDaya card) { private OracleOfMulDaya(final OracleOfMulDaya card) {

View file

@ -40,8 +40,8 @@ public final class PrecognitionField extends CardImpl {
// You may look at the top card of your library. // You may look at the top card of your library.
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast the top card of your library if it's an instant or sorcery card. // You may cast instant and sorcery spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
// {3}: Exile the top card of your library. // {3}: Exile the top card of your library.
this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3))); this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3)));

View file

@ -31,7 +31,7 @@ import java.util.UUID;
*/ */
public final class RadhaHeartOfKeld extends CardImpl { public final class RadhaHeartOfKeld extends CardImpl {
private static final FilterCard filter = new FilterLandCard("play land cards"); private static final FilterCard filter = new FilterLandCard("play lands");
public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) { public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}");
@ -50,9 +50,9 @@ public final class RadhaHeartOfKeld extends CardImpl {
// You may look at the top card of your library any time, and you may play lands from the top of your library. // You may look at the top card of your library any time, and you may play lands from the top of your library.
LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect(); LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect();
lookEffect.overrideRuleText("You may look at the top card of your library any time"); lookEffect.setText("You may look at the top card of your library any time");
PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter); PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter, false);
playEffect.overrideRuleText(", and you may play lands from the top of your library"); playEffect.setText(", and you may play lands from the top of your library");
SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect); SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect);
lookAndPlayAbility.addEffect(playEffect); lookAndPlayAbility.addEffect(playEffect);
@ -61,7 +61,7 @@ public final class RadhaHeartOfKeld extends CardImpl {
// 4RG: Radha gets +X/+X until end of turn, where X is the number of lands you control. // 4RG: Radha gets +X/+X until end of turn, where X is the number of lands you control.
DynamicValue controlledLands = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS); DynamicValue controlledLands = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS);
BoostSourceEffect bse = new BoostSourceEffect(controlledLands, controlledLands, Duration.EndOfTurn, true); BoostSourceEffect bse = new BoostSourceEffect(controlledLands, controlledLands, Duration.EndOfTurn, true);
bse.overrideRuleText("Radha gets +X/+X until end of turn, where X is the number of lands you control"); bse.setText("Radha gets +X/+X until end of turn, where X is the number of lands you control");
this.addAbility(new SimpleActivatedAbility(bse, new ManaCostsImpl("{4}{R}{G}"))); this.addAbility(new SimpleActivatedAbility(bse, new ManaCostsImpl("{4}{R}{G}")));
} }

View file

@ -24,7 +24,7 @@ public final class RamunapExcavator extends CardImpl {
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// You may play land cards from your graveyard. // You may play lands from your graveyard.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect()));
} }

View file

@ -1,6 +1,5 @@
package mage.cards.r; package mage.cards.r;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
@ -16,13 +15,15 @@ import mage.constants.SubType;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate; import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
import java.util.UUID;
/** /**
*
* @author weirddan455 * @author weirddan455
*/ */
public final class Realmwalker extends CardImpl { public final class Realmwalker extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells of the chosen type"); private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells of the chosen type");
static { static {
filter.add(ChosenSubtypePredicate.TRUE); filter.add(ChosenSubtypePredicate.TRUE);
} }
@ -44,7 +45,7 @@ public final class Realmwalker extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells of the chosen type from the top of your library. // You may cast creature spells of the chosen type from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
} }
private Realmwalker(final Realmwalker card) { private Realmwalker(final Realmwalker card) {

View file

@ -11,14 +11,15 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.filter.common.FilterLandCard;
import mage.game.Game; import mage.game.Game;
import java.util.Comparator; import java.util.Comparator;
import java.util.UUID; import java.util.UUID;
/** /**
*
* @author htrajan * @author htrajan
*/ */
public final class VergeRangers extends CardImpl { public final class VergeRangers extends CardImpl {
@ -53,9 +54,11 @@ public final class VergeRangers extends CardImpl {
class VergeRangersEffect extends PlayTheTopCardEffect { class VergeRangersEffect extends PlayTheTopCardEffect {
private static final FilterCard filter = new FilterLandCard("play lands");
public VergeRangersEffect() { public VergeRangersEffect() {
super(StaticFilters.FILTER_CARD_LAND); super(filter, false);
staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library."; staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library";
} }
public VergeRangersEffect(final VergeRangersEffect effect) { public VergeRangersEffect(final VergeRangersEffect effect) {
@ -68,15 +71,16 @@ class VergeRangersEffect extends PlayTheTopCardEffect {
} }
@Override @Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (super.applies(objectId, affectedAbility, source, game, playerId)) { if (!super.applies(objectId, source, affectedControllerId, game)) {
int myLandCount = game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game); return false;
int maxOpponentLandCount = game.getOpponents(playerId).stream()
.map(opponentId -> game.getBattlefield().countAll(StaticFilters.FILTER_LAND, opponentId, game))
.max(Comparator.naturalOrder())
.orElse(0);
return maxOpponentLandCount > myLandCount;
} }
return false;
int myLandCount = game.getBattlefield().countAll(StaticFilters.FILTER_LAND, source.getControllerId(), game);
int maxOpponentLandCount = game.getOpponents(source.getControllerId()).stream()
.map(opponentId -> game.getBattlefield().countAll(StaticFilters.FILTER_LAND, opponentId, game))
.max(Comparator.naturalOrder())
.orElse(0);
return maxOpponentLandCount > myLandCount;
} }
} }

View file

@ -27,6 +27,7 @@ import mage.game.permanent.token.Token;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCardInLibrary;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
@ -51,7 +52,7 @@ public final class VivienMonstersAdvocate extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells from the top of your library. // You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
// +1: Create a 3/3 green Beast creature token. Put your choice of a vigilance // +1: Create a 3/3 green Beast creature token. Put your choice of a vigilance
// counter, a reach counter, or a trample counter on it. // counter, a reach counter, or a trample counter on it.

View file

@ -36,8 +36,8 @@ public final class VizierOfTheMenagerie extends CardImpl {
// You may look at the top card of your library. (You may do this at any time.) // You may look at the top card of your library. (You may do this at any time.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LookAtTopCardOfLibraryAnyTimeEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast the top card of your library if it's a creature card. // You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false)));
// You may spend mana as though it were mana of any type to cast creature spells. // You may spend mana as though it were mana of any type to cast creature spells.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieManaEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieManaEffect()));

View file

@ -94,6 +94,7 @@ public class PlayTopCardFromLibraryTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1); addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
// Double your life total. Target opponent loses half their life, rounded up. // Double your life total. Target opponent loses half their life, rounded up.
checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Revenge", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revenge", playerB); // {4}{W}{B} = 6 life castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revenge", playerB); // {4}{W}{B} = 6 life
setStrictChooseMode(true); setStrictChooseMode(true);
@ -113,6 +114,7 @@ public class PlayTopCardFromLibraryTest extends CardTestPlayerBase {
addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1); addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1);
// Return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. // Return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Revival", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revival", "Balduvian Bears"); // {W/B}{W/B} = 2 life castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revival", "Balduvian Bears"); // {W/B}{W/B} = 2 life
setStrictChooseMode(true); setStrictChooseMode(true);

View file

@ -1,9 +1,5 @@
package mage.abilities.effects; package mage.abilities.effects;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.MageObjectReference; import mage.MageObjectReference;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.DependencyType; import mage.constants.DependencyType;
@ -13,6 +9,11 @@ import mage.constants.SubLayer;
import mage.game.Game; import mage.game.Game;
import mage.target.targetpointer.TargetPointer; import mage.target.targetpointer.TargetPointer;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -46,8 +47,6 @@ public interface ContinuousEffect extends Effect {
SubLayer getSublayer(); SubLayer getSublayer();
void overrideRuleText(String text);
List<MageObjectReference> getAffectedObjects(); List<MageObjectReference> getAffectedObjects();
Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer); Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer);

View file

@ -275,11 +275,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return sublayer; return sublayer;
} }
@Override
public void overrideRuleText(String text) {
this.staticText = text;
}
protected static boolean isCanKill(DynamicValue toughness) { protected static boolean isCanKill(DynamicValue toughness) {
if (toughness instanceof StaticValue) { if (toughness instanceof StaticValue) {
return toughness.calculate(null, null, null) < 0; return toughness.calculate(null, null, null) < 0;

View file

@ -532,7 +532,10 @@ public class ContinuousEffects implements Serializable {
} }
UUID idToCheck; UUID idToCheck;
if (objectToCheck instanceof SplitCardHalf) { if (!type.needPlayCardAbility() && objectToCheck instanceof SplitCardHalf) {
// each split side uses own characteristics to check for playing, all other cases must use main card
// rules:
// 708.4. In every zone except the stack, the characteristics of a split card are those of its two halves combined.
idToCheck = ((SplitCardHalf) objectToCheck).getMainCard().getId(); idToCheck = ((SplitCardHalf) objectToCheck).getMainCard().getId();
} else if (!type.needPlayCardAbility() && objectToCheck instanceof AdventureCardSpell) { } else if (!type.needPlayCardAbility() && objectToCheck instanceof AdventureCardSpell) {
// adventure spell uses alternative characteristics for spell/stack, all other cases must use main card // adventure spell uses alternative characteristics for spell/stack, all other cases must use main card

View file

@ -7,34 +7,44 @@ import mage.constants.AsThoughEffectType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.UUID; import java.util.UUID;
/** /**
* @author nantuko * @author nantuko, JayDi85
*/ */
public class PlayTheTopCardEffect extends AsThoughEffectImpl { public class PlayTheTopCardEffect extends AsThoughEffectImpl {
private final FilterCard filter; private final FilterCard filter;
// can play card or can play lands/cast spells, see two modes below
private final boolean canPlayCardOnly;
public PlayTheTopCardEffect() { public PlayTheTopCardEffect() {
this(StaticFilters.FILTER_CARD); this(new FilterCard("play lands and cast spells"), false);
staticText = "You may play lands and cast spells from the top of your library";
} }
public PlayTheTopCardEffect(FilterCard filter) { public PlayTheTopCardEffect(FilterCard filter, boolean canPlayCardOnly) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter; this.filter = filter;
staticText = "You may " + filter.getMessage() + " from the top of your library"; this.canPlayCardOnly = canPlayCardOnly;
this.staticText = "You may " + filter.getMessage() + " from the top of your library";
// verify check: if you see "card" text in the rules then use card mode
// (there aren't any real cards after oracle update, but can be added in the future)
if (this.canPlayCardOnly != filter.getMessage().toLowerCase(Locale.ENGLISH).contains("card")) {
throw new IllegalArgumentException("Wrong usage of card mode settings");
}
} }
public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) { public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) {
super(effect); super(effect);
this.filter = effect.filter; this.filter = effect.filter;
this.canPlayCardOnly = effect.canPlayCardOnly;
} }
@Override @Override
@ -49,25 +59,45 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
@Override @Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return applies(objectId, null, source, game, affectedControllerId); // main card and all parts are checks in different calls.
} // two modes:
// * can play cards (must check main card and allows any parts)
// * can play lands/spells (must check specific part and allows specific part)
@Override // current card's part
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
Card cardToCheck = game.getCard(objectId); Card cardToCheck = game.getCard(objectId);
objectId = CardUtil.getMainCardId(game, objectId); // for split cards if (cardToCheck == null) {
return false;
if (cardToCheck != null
&& playerId.equals(source.getControllerId())
&& cardToCheck.isOwnedBy(source.getControllerId())
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
&& filter.match(cardToCheck, source.getSourceId(), source.getControllerId(), game)) {
Player player = game.getPlayer(cardToCheck.getOwnerId());
UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId();
return objectId.equals(needCardID);
} }
return false;
}
if (this.canPlayCardOnly) {
// check whole card intead part
cardToCheck = cardToCheck.getMainCard();
}
// must be you
if (!affectedControllerId.equals(source.getControllerId())) {
return false;
}
// must be your card
Player player = game.getPlayer(cardToCheck.getOwnerId());
if (player == null) {
return false;
}
// must be from your library
Card topCard = player.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
// can't cast without mana cost
if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) {
return false;
}
// must be correct card
return filter.match(cardToCheck, source.getSourceId(), source.getControllerId(), game);
}
} }

View file

@ -10,7 +10,6 @@ import mage.filter.FilterCard;
import mage.filter.common.FilterLandCard; import mage.filter.common.FilterLandCard;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -28,7 +27,7 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
public PlayLandsFromGraveyardControllerEffect(FilterCard filter) { public PlayLandsFromGraveyardControllerEffect(FilterCard filter) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter; this.filter = filter;
staticText = "You may play " + filter.getMessage() + " from your graveyard"; this.staticText = "You may play " + filter.getMessage() + " from your graveyard";
} }
public PlayLandsFromGraveyardControllerEffect(final PlayLandsFromGraveyardControllerEffect effect) { public PlayLandsFromGraveyardControllerEffect(final PlayLandsFromGraveyardControllerEffect effect) {
@ -49,30 +48,35 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
@Override @Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return applies(objectId, null, source, game, affectedControllerId); // current card's part
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
Card cardToCheck = game.getCard(objectId); Card cardToCheck = game.getCard(objectId);
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
if (cardToCheck == null) { if (cardToCheck == null) {
return false; return false;
} }
// must be you
if (!affectedControllerId.equals(source.getControllerId())) {
return false;
}
// must be your card
Player player = game.getPlayer(cardToCheck.getOwnerId()); Player player = game.getPlayer(cardToCheck.getOwnerId());
if (player == null) { if (player == null) {
return false; return false;
} }
UUID needCardId = objectId; // must be from your graveyard
UUID needCardId = cardToCheck.getMainCard().getId();
if (player.getGraveyard().getCards(game).stream().noneMatch(c -> c.getId().equals(needCardId))) { if (player.getGraveyard().getCards(game).stream().noneMatch(c -> c.getId().equals(needCardId))) {
return false; return false;
} }
return playerId.equals(source.getControllerId()) // can't cast without mana cost
&& cardToCheck.isOwnedBy(source.getControllerId()) if (!cardToCheck.isLand() && cardToCheck.getManaCost().isEmpty()) {
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand()) return false;
&& filter.match(cardToCheck, game); }
// must be correct card
return filter.match(cardToCheck, source.getSourceId(), source.getControllerId(), game);
} }
} }

View file

@ -1,5 +1,3 @@
package mage.abilities.keyword; package mage.abilities.keyword;
import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -10,19 +8,18 @@ import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public class ProwessAbility extends SpellCastControllerTriggeredAbility { public class ProwessAbility extends SpellCastControllerTriggeredAbility {
private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell"); private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell");
static { static {
filterNonCreatureSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); filterNonCreatureSpell.add(Predicates.not(CardType.CREATURE.getPredicate()));
} }
public ProwessAbility() { public ProwessAbility() {
super(new BoostSourceEffect(1,1,Duration.EndOfTurn), false); super(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false);
this.filter = filterNonCreatureSpell; this.filter = filterNonCreatureSpell;
} }