refactor: PlayFromGraveyardControllerEffect (#13032)

* refactor PlayFromGraveyardControllerEffect for broader usage

* AbandonedSarcophagus to common class

* refactor Player::canPlayCardsFromGraveyard to AsThough common class

* a few more refactors
This commit is contained in:
xenohedron 2024-10-26 16:20:16 -04:00 committed by GitHub
parent 0d63750a81
commit 001f9e866f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 125 additions and 354 deletions

View file

@ -20,6 +20,7 @@ public class PlayFromNotOwnHandZoneAllEffect extends AsThoughEffectImpl {
private final boolean onlyOwnedCards;
private final TargetController allowedCaster;
@Deprecated // Only used in some tests - should be refactored and removed
public PlayFromNotOwnHandZoneAllEffect(FilterCard filter, Zone fromZone, boolean onlyOwnedCards, TargetController allowedCaster, Duration duration) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.filter = filter;

View file

@ -15,23 +15,58 @@ import mage.players.Player;
import java.util.UUID;
/**
* @author JayDi85
* @author JayDi85, xenohedron
*/
public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
public class PlayFromGraveyardControllerEffect extends AsThoughEffectImpl {
private static final FilterCard filterPlayLands = new FilterLandCard("lands");
private static final FilterCard filterPlayCast = new FilterCard("play lands and cast spells");
private final FilterCard filter;
public PlayLandsFromGraveyardControllerEffect() {
this(new FilterLandCard("lands"));
/**
* You may play lands from your graveyard.
*/
public static PlayFromGraveyardControllerEffect playLands() {
return new PlayFromGraveyardControllerEffect(filterPlayLands);
}
public PlayLandsFromGraveyardControllerEffect(FilterCard filter) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
/**
* You may play lands and cast spells from your graveyard.
*/
public static PlayFromGraveyardControllerEffect playLandsAndCastSpells(Duration duration) {
return new PlayFromGraveyardControllerEffect(filterPlayCast, duration);
}
/**
* You may [play/cast xxx] from your graveyard.
*/
public PlayFromGraveyardControllerEffect(FilterCard filter) {
this(filter, Duration.WhileOnBattlefield);
}
/**
* [Until duration,] you may [play/cast xxx] from your graveyard.
*/
public PlayFromGraveyardControllerEffect(FilterCard filter, Duration duration) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.filter = filter;
this.staticText = "You may play " + filter.getMessage() + " from your graveyard";
String filterMessage = filter.getMessage();
if (!filterMessage.startsWith("play ") && !filterMessage.startsWith("cast")) {
if (filterMessage.contains("lands")) {
filterMessage = "play " + filterMessage;
} else {
filterMessage = "cast " + filterMessage;
}
}
String durationString = duration.toString();
if (!durationString.isEmpty()) {
durationString += ", ";
}
this.staticText = durationString + "you may " + filterMessage + " from your graveyard";
}
protected PlayLandsFromGraveyardControllerEffect(final PlayLandsFromGraveyardControllerEffect effect) {
protected PlayFromGraveyardControllerEffect(final PlayFromGraveyardControllerEffect effect) {
super(effect);
this.filter = effect.filter;
}
@ -41,16 +76,16 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
return true;
}
@Override
public PlayLandsFromGraveyardControllerEffect copy() {
return new PlayLandsFromGraveyardControllerEffect(this);
public PlayFromGraveyardControllerEffect copy() {
return new PlayFromGraveyardControllerEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
// current card's part
@ -80,7 +115,7 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {
return false;
}
if (affectedAbility instanceof SpellAbility){
if (affectedAbility instanceof SpellAbility) {
cardToCheck = ((SpellAbility) affectedAbility).getCharacteristics(game);
}
// must be correct card

View file

@ -1,27 +1,22 @@
package mage.game.command.emblems;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.cards.Card;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.abilities.effects.common.ruleModifying.PlayFromGraveyardControllerEffect;
import mage.constants.Zone;
import mage.game.Game;
import mage.filter.common.FilterPermanentCard;
import mage.game.command.Emblem;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class WrennAndRealmbreakerEmblem extends Emblem {
private static final FilterPermanentCard filter = new FilterPermanentCard("play lands and cast permanent spells");
// -7: You get an emblem with "You may play lands and cast permanent spells from your graveyard."
public WrennAndRealmbreakerEmblem() {
super("Emblem Wrenn");
this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new WrennAndRealmbreakerEmblemEffect()));
this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new PlayFromGraveyardControllerEffect(filter)));
}
private WrennAndRealmbreakerEmblem(final WrennAndRealmbreakerEmblem card) {
@ -33,37 +28,3 @@ public final class WrennAndRealmbreakerEmblem extends Emblem {
return new WrennAndRealmbreakerEmblem(this);
}
}
class WrennAndRealmbreakerEmblemEffect extends AsThoughEffectImpl {
public WrennAndRealmbreakerEmblemEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "you may play lands and cast permanent spells from your graveyard";
}
protected WrennAndRealmbreakerEmblemEffect(final WrennAndRealmbreakerEmblemEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public WrennAndRealmbreakerEmblemEffect copy() {
return new WrennAndRealmbreakerEmblemEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (!source.isControlledBy(affectedControllerId)) {
return false;
}
Card card = game.getCard(objectId);
return card != null
&& card.isPermanent(game)
&& card.isOwnedBy(source.getControllerId())
&& game.getState().getZone(objectId) == Zone.GRAVEYARD;
}
}

View file

@ -196,10 +196,6 @@ public interface Player extends MageItem, Copyable<Player> {
boolean canLoseByZeroOrLessLife();
void setPlayCardsFromGraveyard(boolean playCardsFromGraveyard);
boolean canPlayCardsFromGraveyard();
void setPlotFromTopOfLibrary(boolean canPlotFromTopOfLibrary);
boolean canPlotFromTopOfLibrary();

View file

@ -152,7 +152,6 @@ public abstract class PlayerImpl implements Player, Serializable {
protected boolean canLoseLife = true;
protected PayLifeCostLevel payLifeCostLevel = PayLifeCostLevel.allAbilities;
protected boolean loseByZeroOrLessLife = true;
protected boolean canPlayCardsFromGraveyard = true;
protected boolean canPlotFromTopOfLibrary = false;
protected boolean drawsFromBottom = false;
protected boolean drawsOnOpponentsTurn = false;
@ -252,7 +251,6 @@ public abstract class PlayerImpl implements Player, Serializable {
this.canGainLife = player.canGainLife;
this.canLoseLife = player.canLoseLife;
this.loseByZeroOrLessLife = player.loseByZeroOrLessLife;
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard;
this.canPlotFromTopOfLibrary = player.canPlotFromTopOfLibrary;
this.drawsFromBottom = player.drawsFromBottom;
this.drawsOnOpponentsTurn = player.drawsOnOpponentsTurn;
@ -367,7 +365,6 @@ public abstract class PlayerImpl implements Player, Serializable {
this.sacrificeCostFilter = player.getSacrificeCostFilter() != null
? player.getSacrificeCostFilter().copy() : null;
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard();
this.canPlotFromTopOfLibrary = player.canPlotFromTopOfLibrary();
this.drawsFromBottom = player.isDrawsFromBottom();
this.drawsOnOpponentsTurn = player.isDrawsOnOpponentsTurn();
@ -482,7 +479,6 @@ public abstract class PlayerImpl implements Player, Serializable {
this.canLoseLife = true;
this.payLifeCostLevel = PayLifeCostLevel.allAbilities;
this.loseByZeroOrLessLife = true;
this.canPlayCardsFromGraveyard = true;
this.canPlotFromTopOfLibrary = false;
this.drawsFromBottom = false;
this.drawsOnOpponentsTurn = false;
@ -526,7 +522,6 @@ public abstract class PlayerImpl implements Player, Serializable {
this.payLifeCostLevel = PayLifeCostLevel.allAbilities;
this.sacrificeCostFilter = null;
this.loseByZeroOrLessLife = true;
this.canPlayCardsFromGraveyard = false;
this.canPlotFromTopOfLibrary = false;
this.drawsFromBottom = false;
this.drawsOnOpponentsTurn = false;
@ -4138,9 +4133,7 @@ public abstract class PlayerImpl implements Player, Serializable {
approvingObjects = new HashSet<>();
}
boolean canActivateAsHandZone = !approvingObjects.isEmpty()
|| (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
boolean possibleToPlay = canActivateAsHandZone
boolean possibleToPlay = !approvingObjects.isEmpty()
&& ability.getZone().match(Zone.HAND)
&& (isPlaySpell || isPlayLand);
@ -4164,7 +4157,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
// from non hand mode (with affected controller)
if (canActivateAsHandZone && ability.getControllerId() != this.getId()) {
if (!approvingObjects.isEmpty() && ability.getControllerId() != this.getId()) {
UUID savedControllerId = ability.getControllerId();
ability.setControllerId(this.getId());
try {
@ -4646,16 +4639,6 @@ public abstract class PlayerImpl implements Player, Serializable {
this.loseByZeroOrLessLife = loseByZeroOrLessLife;
}
@Override
public boolean canPlayCardsFromGraveyard() {
return canPlayCardsFromGraveyard;
}
@Override
public void setPlayCardsFromGraveyard(boolean playCardsFromGraveyard) {
this.canPlayCardsFromGraveyard = playCardsFromGraveyard;
}
@Override
public boolean canPlotFromTopOfLibrary() {
return canPlotFromTopOfLibrary;