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

@ -1,9 +1,5 @@
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.abilities.Ability;
import mage.constants.DependencyType;
@ -13,6 +9,11 @@ import mage.constants.SubLayer;
import mage.game.Game;
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
*/
@ -46,8 +47,6 @@ public interface ContinuousEffect extends Effect {
SubLayer getSublayer();
void overrideRuleText(String text);
List<MageObjectReference> getAffectedObjects();
Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer);

View file

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

View file

@ -532,7 +532,10 @@ public class ContinuousEffects implements Serializable {
}
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();
} else if (!type.needPlayCardAbility() && objectToCheck instanceof AdventureCardSpell) {
// 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.Outcome;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.UUID;
/**
* @author nantuko
* @author nantuko, JayDi85
*/
public class PlayTheTopCardEffect extends AsThoughEffectImpl {
private final FilterCard filter;
// can play card or can play lands/cast spells, see two modes below
private final boolean canPlayCardOnly;
public PlayTheTopCardEffect() {
this(StaticFilters.FILTER_CARD);
staticText = "You may play lands and cast spells from the top of your library";
this(new FilterCard("play lands and cast spells"), false);
}
public PlayTheTopCardEffect(FilterCard filter) {
public PlayTheTopCardEffect(FilterCard filter, boolean canPlayCardOnly) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
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) {
super(effect);
this.filter = effect.filter;
this.canPlayCardOnly = effect.canPlayCardOnly;
}
@Override
@ -49,25 +59,45 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
@Override
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
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
// current card's part
Card cardToCheck = game.getCard(objectId);
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
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);
if (cardToCheck == null) {
return false;
}
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.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
@ -28,7 +27,7 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
public PlayLandsFromGraveyardControllerEffect(FilterCard filter) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
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) {
@ -49,30 +48,35 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return applies(objectId, null, source, game, affectedControllerId);
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
// current card's part
Card cardToCheck = game.getCard(objectId);
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
if (cardToCheck == null) {
return false;
}
// 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;
}
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))) {
return false;
}
return playerId.equals(source.getControllerId())
&& cardToCheck.isOwnedBy(source.getControllerId())
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
&& filter.match(cardToCheck, game);
// 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

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