Refactoring cards that mill (WIP, do not merge) (#6713)

* added mill method

* updated mill effects to use new method

* refactored individual cards

* small updated to Grindstone and Sphinx's Tutelage

* another updated to Grindstone

* fixed a test

* fixed Countermand null check

* more refactoring

* updated dredge ability to use mill
This commit is contained in:
Evan Kranzler 2020-06-24 07:50:00 -04:00 committed by GitHub
parent 8b5f4f28f0
commit 785be83484
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 738 additions and 957 deletions

View file

@ -4,7 +4,6 @@ import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.cards.Card;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -38,8 +37,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl {
Player player = game.getPlayer(controllerId);
if (player != null && player.getLibrary().size() >= numberOfCards) {
paid = true;
this.cardsMovedToGraveyard.addAll(player.getLibrary().getTopCards(game, numberOfCards));
player.moveCards(player.getLibrary().getTopCards(game, numberOfCards), Zone.GRAVEYARD, ability, game);
this.cardsMovedToGraveyard.addAll(player.millCards(numberOfCards, ability, game).getCards(game));
}
return paid;
}

View file

@ -6,7 +6,6 @@ import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -45,7 +44,7 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source));
if (player != null) {
player.moveCards(player.getLibrary().getTopCards(game, amount.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
player.millCards(amount.calculate(game, source, this), source, game);
return true;
}
return false;

View file

@ -3,7 +3,6 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -36,7 +35,7 @@ public class PutTopCardOfLibraryIntoGraveControllerEffect extends OneShotEffect
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
return controller.moveCards(controller.getLibrary().getTopCards(game, numberCards), Zone.GRAVEYARD, source, game);
return !controller.millCards(numberCards, source, game).isEmpty();
}
return false;
}

View file

@ -6,7 +6,6 @@ import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -76,7 +75,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect
private void putCardsToGravecard(UUID playerId, Ability source, Game game) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
player.millCards(numberCards.calculate(game, source, this), source, game);
}
}

View file

@ -5,7 +5,6 @@ import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -41,7 +40,7 @@ public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source));
if (player != null) {
player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
player.millCards(numberCards.calculate(game, source, this), source, game);
return true;
}
return false;

View file

@ -4,8 +4,6 @@ import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -13,6 +11,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.players.Player;
import mage.util.CardUtil;
/**
* If you would draw a card, instead you may put exactly X cards from the top of
@ -44,9 +43,9 @@ class DredgeEffect extends ReplacementEffectImpl {
public DredgeEffect(int value) {
super(Duration.WhileInGraveyard, Outcome.AIDontUseIt);
this.amount = value;
this.staticText = ("Dredge ") + Integer.toString(value) + " <i>(If you would draw a card, instead you may put exactly "
+ value + " card(s) from the top of your library into your graveyard. If you do, return this card from "
+ "your graveyard to your hand. Otherwise, draw a card.)</i>";
this.staticText = "Dredge " + value + " <i>(If you would draw a card, you may mill "
+ CardUtil.numberToText(value, "a") + (value > 1 ? " cards" : " card")
+ " instead. If you do, return this card from your graveyard to your hand.)</i>";
}
public DredgeEffect(final DredgeEffect effect) {
@ -74,13 +73,11 @@ class DredgeEffect extends ReplacementEffectImpl {
if (owner != null
&& owner.getLibrary().size() >= amount
&& owner.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()).
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(owner.getLogName()).append(" dredges ").append(sourceCard.getLogName()).toString());
}
Cards cardsToGrave = new CardsImpl();
cardsToGrave.addAll(owner.getLibrary().getTopCards(game, amount));
owner.moveCards(cardsToGrave, Zone.GRAVEYARD, source, game);
owner.millCards(amount, source, game);
owner.moveCards(sourceCard, Zone.HAND, source, game);
return true;
}

View file

@ -531,10 +531,10 @@ public interface Player extends MageItem, Copyable<Player> {
* @param cards - list of cards that have to be moved
* @param game - game
* @param anyOrder - true = if player can determine the order of the cards
* else false = random order
* 401.4. If an effect puts two or more cards in a specific position in a library
* at the same time, the owner of those cards may arrange them in any order.
* That librarys owner doesnt reveal the order in which the cards go into the library.
* else false = random order
* 401.4. If an effect puts two or more cards in a specific position in a library
* at the same time, the owner of those cards may arrange them in any order.
* That librarys owner doesnt reveal the order in which the cards go into the library.
* @param source - source ability
* @return
*/
@ -563,13 +563,13 @@ public interface Player extends MageItem, Copyable<Player> {
* @return
*/
boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder);
boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source);
boolean shuffleCardsToLibrary(Card card, Game game, Ability source);
// set the value for X mana spells and abilities
default int announceXMana(int min, int max, String message, Game game, Ability ability) {
return announceXMana(min, max, 1, message, game, ability);
@ -840,6 +840,8 @@ public interface Player extends MageItem, Copyable<Player> {
*/
boolean moveCardToCommandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone);
Cards millCards(int toMill, Ability source, Game game);
/**
* Checks if the playerToCheckId is from an opponent in range
*

View file

@ -4193,6 +4193,17 @@ public abstract class PlayerImpl implements Player, Serializable {
return result;
}
@Override
public Cards millCards(int toMill, Ability source, Game game) {
GameEvent event = GameEvent.getEvent(EventType.MILL_CARDS, getId(), source.getSourceId(), getId(), toMill);
if (game.replaceEvent(event)) {
return new CardsImpl();
}
Cards cards = new CardsImpl(this.getLibrary().getTopCards(game, event.getAmount()));
this.moveCards(cards, Zone.GRAVEYARD, source, game);
return cards;
}
@Override
public boolean hasOpponent(UUID playerToCheckId, Game game) {
return !this.getId().equals(playerToCheckId)