[MID] updated Teferi, Who Slows the Sunset emblem

This commit is contained in:
Evan Kranzler 2021-09-22 09:39:21 -04:00
parent aec68ac9d5
commit 4ab41a5b12
7 changed files with 120 additions and 38 deletions

View file

@ -2571,7 +2571,7 @@ public class TestPlayer implements Player {
// library // library
if (target.getOriginalTarget() instanceof TargetCardInLibrary if (target.getOriginalTarget() instanceof TargetCardInLibrary
|| (target.getOriginalTarget() instanceof TargetCard && target.getOriginalTarget().getZone() == Zone.LIBRARY)) { || (target.getOriginalTarget() instanceof TargetCard && target.getOriginalTarget().getZone() == Zone.LIBRARY)) {
// user don't have access to library, so it must be targeted through list/revealed cards // user don't have access to library, so it must be targeted through list/revealed cards
Assert.fail("Library zone is private, you must target through cards list, e.g. revealed: " + target.getOriginalTarget().getClass().getCanonicalName()); Assert.fail("Library zone is private, you must target through cards list, e.g. revealed: " + target.getOriginalTarget().getClass().getCanonicalName());
} }
@ -3714,6 +3714,16 @@ public class TestPlayer implements Player {
return computerPlayer.canPlayCardsFromGraveyard(); return computerPlayer.canPlayCardsFromGraveyard();
} }
@Override
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
computerPlayer.setDrawsOnOpponentsTurn(drawsOnOpponentsTurn);
}
@Override
public boolean isDrawsOnOpponentsTurn() {
return computerPlayer.isDrawsOnOpponentsTurn();
}
@Override @Override
public void setPayManaMode(boolean payManaMode) { public void setPayManaMode(boolean payManaMode) {
computerPlayer.setPayManaMode(payManaMode); computerPlayer.setPayManaMode(payManaMode);

View file

@ -236,6 +236,16 @@ public class PlayerStub implements Player {
return false; return false;
} }
@Override
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
}
@Override
public boolean isDrawsOnOpponentsTurn() {
return false;
}
@Override @Override
public List<AlternativeSourceCosts> getAlternativeSourceCosts() { public List<AlternativeSourceCosts> getAlternativeSourceCosts() {
return null; return null;

View file

@ -317,6 +317,12 @@ public final class StaticFilters {
FILTER_CONTROLLED_A_PERMANENT.setLockedFilter(true); FILTER_CONTROLLED_A_PERMANENT.setLockedFilter(true);
} }
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENTS = new FilterControlledPermanent("permanents you control");
static {
FILTER_CONTROLLED_PERMANENTS.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_SHORT_TEXT = new FilterControlledPermanent("permanent"); public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_SHORT_TEXT = new FilterControlledPermanent("permanent");
static { static {

View file

@ -1,19 +1,49 @@
package mage.game.command.emblems; package mage.game.command.emblems;
import mage.abilities.common.BeginningOfDrawTriggeredAbility; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.common.continuous.UntapAllDuringEachOtherPlayersUntapStepEffect; import mage.abilities.effects.common.continuous.UntapAllDuringEachOtherPlayersUntapStepEffect;
import mage.constants.TargetController; import mage.constants.*;
import mage.constants.Zone; import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent; import mage.game.Game;
import mage.game.command.Emblem; import mage.game.command.Emblem;
import mage.players.Player;
public class TeferiWhoSlowsTheSunsetEmblem extends Emblem { public class TeferiWhoSlowsTheSunsetEmblem extends Emblem {
// You get an emblem with "Untap all permanents you control during each opponent's untap step" and "You draw a card during each opponent's draw step." // You get an emblem with "Untap all permanents you control during each opponent's untap step" and "You draw a card during each opponent's draw step."
public TeferiWhoSlowsTheSunsetEmblem() { public TeferiWhoSlowsTheSunsetEmblem() {
this.setName("Emblem Teferi"); this.setName("Emblem Teferi");
this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new UntapAllDuringEachOtherPlayersUntapStepEffect(new FilterControlledPermanent("permanents you control")))); this.getAbilities().add(new SimpleStaticAbility(
this.getAbilities().add(new BeginningOfDrawTriggeredAbility(Zone.COMMAND, new DrawCardSourceControllerEffect(1), TargetController.OPPONENT, false)); Zone.COMMAND, new UntapAllDuringEachOtherPlayersUntapStepEffect(StaticFilters.FILTER_CONTROLLED_PERMANENTS)
));
this.getAbilities().add(new SimpleStaticAbility(new TeferiWhoSlowsTheSunsetEmblemEffect()));
}
}
class TeferiWhoSlowsTheSunsetEmblemEffect extends ContinuousEffectImpl {
TeferiWhoSlowsTheSunsetEmblemEffect() {
super(Duration.EndOfGame, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit);
staticText = "you draw a card during each opponent's draw step";
}
private TeferiWhoSlowsTheSunsetEmblemEffect(final TeferiWhoSlowsTheSunsetEmblemEffect effect) {
super(effect);
}
@Override
public TeferiWhoSlowsTheSunsetEmblemEffect copy() {
return new TeferiWhoSlowsTheSunsetEmblemEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
player.setDrawsOnOpponentsTurn(true);
return true;
} }
} }

View file

@ -29,6 +29,14 @@ public class DrawStep extends Step {
//20091005 - 504.1/703.4c //20091005 - 504.1/703.4c
activePlayer.drawCards(1, null, game); activePlayer.drawCards(1, null, game);
game.applyEffects(); game.applyEffects();
for (UUID playerId : game.getState().getPlayersInRange(activePlayerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null
&& player.isDrawsOnOpponentsTurn()
&& player.hasOpponent(activePlayerId, game)) {
player.drawCards(1, null, game);
}
}
super.beginStep(game, activePlayerId); super.beginStep(game, activePlayerId);
} }

View file

@ -178,6 +178,10 @@ public interface Player extends MageItem, Copyable<Player> {
boolean canPlayCardsFromGraveyard(); boolean canPlayCardsFromGraveyard();
void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn);
boolean isDrawsOnOpponentsTurn();
/** /**
* Returns alternative casting costs a player can cast spells for * Returns alternative casting costs a player can cast spells for
* *

View file

@ -138,6 +138,7 @@ public abstract class PlayerImpl implements Player, Serializable {
protected boolean canPayLifeCost = true; protected boolean canPayLifeCost = true;
protected boolean loseByZeroOrLessLife = true; protected boolean loseByZeroOrLessLife = true;
protected boolean canPlayCardsFromGraveyard = true; protected boolean canPlayCardsFromGraveyard = true;
protected boolean drawsOnOpponentsTurn = false;
protected FilterPermanent sacrificeCostFilter; protected FilterPermanent sacrificeCostFilter;
@ -239,6 +240,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.canLoseLife = player.canLoseLife; this.canLoseLife = player.canLoseLife;
this.loseByZeroOrLessLife = player.loseByZeroOrLessLife; this.loseByZeroOrLessLife = player.loseByZeroOrLessLife;
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard; this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard;
this.drawsOnOpponentsTurn = player.drawsOnOpponentsTurn;
this.attachments.addAll(player.attachments); this.attachments.addAll(player.attachments);
@ -347,6 +349,7 @@ public abstract class PlayerImpl implements Player, Serializable {
? player.getSacrificeCostFilter().copy() : null; ? player.getSacrificeCostFilter().copy() : null;
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife(); this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard(); this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard();
this.drawsOnOpponentsTurn = player.isDrawsOnOpponentsTurn();
this.alternativeSourceCosts.clear(); this.alternativeSourceCosts.clear();
this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts());
@ -470,6 +473,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.sacrificeCostFilter = null; this.sacrificeCostFilter = null;
this.loseByZeroOrLessLife = true; this.loseByZeroOrLessLife = true;
this.canPlayCardsFromGraveyard = false; this.canPlayCardsFromGraveyard = false;
this.drawsOnOpponentsTurn = false;
this.topCardRevealed = false; this.topCardRevealed = false;
this.alternativeSourceCosts.clear(); this.alternativeSourceCosts.clear();
this.clearCastSourceIdManaCosts(); this.clearCastSourceIdManaCosts();
@ -637,9 +641,9 @@ public abstract class PlayerImpl implements Player, Serializable {
&& this.hasOpponent(sourceControllerId, game) && this.hasOpponent(sourceControllerId, game)
&& game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
&& abilities.stream() && abilities.stream()
.filter(HexproofBaseAbility.class::isInstance) .filter(HexproofBaseAbility.class::isInstance)
.map(HexproofBaseAbility.class::cast) .map(HexproofBaseAbility.class::cast)
.anyMatch(ability -> ability.checkObject(source, game))) { .anyMatch(ability -> ability.checkObject(source, game))) {
return false; return false;
} }
@ -679,7 +683,7 @@ public abstract class PlayerImpl implements Player, Serializable {
game.informPlayers(getLogName() + " discards down to " game.informPlayers(getLogName() + " discards down to "
+ this.maxHandSize + this.maxHandSize
+ (this.maxHandSize == 1 + (this.maxHandSize == 1
? " hand card" : " hand cards")); ? " hand card" : " hand cards"));
} }
discard(hand.size() - this.maxHandSize, false, false, null, game); discard(hand.size() - this.maxHandSize, false, false, null, game);
} }
@ -1152,7 +1156,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/** /**
* @param originalAbility * @param originalAbility
* @param game * @param game
* @param noMana cast it without paying mana costs * @param noMana cast it without paying mana costs
* @param approvingObject which object approved the cast * @param approvingObject which object approved the cast
* @return * @return
*/ */
@ -2914,7 +2918,7 @@ public abstract class PlayerImpl implements Player, Serializable {
* @return * @return
*/ */
private Object rollDieInner(Outcome outcome, Game game, Ability source, RollDieType rollDieType, private Object rollDieInner(Outcome outcome, Game game, Ability source, RollDieType rollDieType,
int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount) { int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount) {
if (rollsAmount == 1) { if (rollsAmount == 1) {
return rollDieInnerWithReplacement(game, source, rollDieType, sidesAmount, chaosSidesAmount, planarSidesAmount); return rollDieInnerWithReplacement(game, source, rollDieType, sidesAmount, chaosSidesAmount, planarSidesAmount);
} }
@ -3010,8 +3014,8 @@ public abstract class PlayerImpl implements Player, Serializable {
* @param outcome * @param outcome
* @param source * @param source
* @param game * @param game
* @param sidesAmount number of sides the dice has * @param sidesAmount number of sides the dice has
* @param rollsAmount number of tries to roll the dice * @param rollsAmount number of tries to roll the dice
* @param ignoreLowestAmount remove the lowest rolls from the results * @param ignoreLowestAmount remove the lowest rolls from the results
* @return the number that the player rolled * @return the number that the player rolled
*/ */
@ -3029,18 +3033,18 @@ public abstract class PlayerImpl implements Player, Serializable {
* @param outcome * @param outcome
* @param source * @param source
* @param game * @param game
* @param rollDieType die type to roll, e.g. planar or numerical * @param rollDieType die type to roll, e.g. planar or numerical
* @param sidesAmount sides per die * @param sidesAmount sides per die
* @param chaosSidesAmount for planar die: chaos sides * @param chaosSidesAmount for planar die: chaos sides
* @param planarSidesAmount for planar die: planar sides * @param planarSidesAmount for planar die: planar sides
* @param rollsAmount rolls * @param rollsAmount rolls
* @param ignoreLowestAmount for numerical die: ignore multiple rolls with * @param ignoreLowestAmount for numerical die: ignore multiple rolls with
* the lowest values * the lowest values
* @return * @return
*/ */
private List<Object> rollDiceInner(Outcome outcome, Ability source, Game game, RollDieType rollDieType, private List<Object> rollDiceInner(Outcome outcome, Ability source, Game game, RollDieType rollDieType,
int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int sidesAmount, int chaosSidesAmount, int planarSidesAmount,
int rollsAmount, int ignoreLowestAmount) { int rollsAmount, int ignoreLowestAmount) {
RollDiceEvent rollDiceEvent = new RollDiceEvent(source, rollDieType, sidesAmount, rollsAmount); RollDiceEvent rollDiceEvent = new RollDiceEvent(source, rollDieType, sidesAmount, rollsAmount);
if (ignoreLowestAmount > 0) { if (ignoreLowestAmount > 0) {
rollDiceEvent.incIgnoreLowestAmount(ignoreLowestAmount); rollDiceEvent.incIgnoreLowestAmount(ignoreLowestAmount);
@ -3200,10 +3204,10 @@ public abstract class PlayerImpl implements Player, Serializable {
/** /**
* @param source * @param source
* @param game * @param game
* @param chaosSidesAmount The number of chaos sides the planar die * @param chaosSidesAmount The number of chaos sides the planar die
* currently has (normally 1 but can be 5) * currently has (normally 1 but can be 5)
* @param planarSidesAmount The number of chaos sides the planar die * @param planarSidesAmount The number of chaos sides the planar die
* currently has (normally 1) * currently has (normally 1)
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
* or BlankRoll * or BlankRoll
*/ */
@ -3261,7 +3265,7 @@ public abstract class PlayerImpl implements Player, Serializable {
for (Card card : getHand().getCards(game)) { for (Card card : getHand().getCards(game)) {
Abilities<ActivatedManaAbilityImpl> manaAbilities Abilities<ActivatedManaAbilityImpl> manaAbilities
= card.getAbilities(game).getAvailableActivatedManaAbilities(Zone.HAND, playerId, game); = card.getAbilities(game).getAvailableActivatedManaAbilities(Zone.HAND, playerId, game);
for (Iterator<ActivatedManaAbilityImpl> it = manaAbilities.iterator(); it.hasNext();) { for (Iterator<ActivatedManaAbilityImpl> it = manaAbilities.iterator(); it.hasNext(); ) {
ActivatedManaAbilityImpl ability = it.next(); ActivatedManaAbilityImpl ability = it.next();
Abilities<ActivatedManaAbilityImpl> noTapAbilities = new AbilitiesImpl<>(ability); Abilities<ActivatedManaAbilityImpl> noTapAbilities = new AbilitiesImpl<>(ability);
if (ability.getManaCosts().isEmpty() && !ability.isPoolDependant()) { if (ability.getManaCosts().isEmpty() && !ability.isPoolDependant()) {
@ -3278,7 +3282,7 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean useLater = false; // sources with mana costs or mana pool dependency boolean useLater = false; // sources with mana costs or mana pool dependency
Abilities<ActivatedManaAbilityImpl> manaAbilities Abilities<ActivatedManaAbilityImpl> manaAbilities
= permanent.getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game); // returns ability only if canActivate is true = permanent.getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game); // returns ability only if canActivate is true
for (Iterator<ActivatedManaAbilityImpl> it = manaAbilities.iterator(); it.hasNext();) { for (Iterator<ActivatedManaAbilityImpl> it = manaAbilities.iterator(); it.hasNext(); ) {
ActivatedManaAbilityImpl ability = it.next(); ActivatedManaAbilityImpl ability = it.next();
if (canUse == null) { if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game); canUse = permanent.canUseActivatedAbilities(game);
@ -3320,7 +3324,7 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean usePoolDependantAbilities = false; // use such abilities later than other if possible because it can maximize mana production boolean usePoolDependantAbilities = false; // use such abilities later than other if possible because it can maximize mana production
while (anAbilityWasUsed && !sourceWithCosts.isEmpty()) { while (anAbilityWasUsed && !sourceWithCosts.isEmpty()) {
anAbilityWasUsed = false; anAbilityWasUsed = false;
for (Iterator<Abilities<ActivatedManaAbilityImpl>> iterator = sourceWithCosts.iterator(); iterator.hasNext();) { for (Iterator<Abilities<ActivatedManaAbilityImpl>> iterator = sourceWithCosts.iterator(); iterator.hasNext(); ) {
Abilities<ActivatedManaAbilityImpl> manaAbilities = iterator.next(); Abilities<ActivatedManaAbilityImpl> manaAbilities = iterator.next();
if (usePoolDependantAbilities || !manaAbilities.hasPoolDependantAbilities()) { if (usePoolDependantAbilities || !manaAbilities.hasPoolDependantAbilities()) {
boolean used; boolean used;
@ -3356,7 +3360,7 @@ public abstract class PlayerImpl implements Player, Serializable {
* and cleared thereafter * and cleared thereafter
* *
* @param netManaAvailable the net mana produced by the triggered mana * @param netManaAvailable the net mana produced by the triggered mana
* abaility * abaility
*/ */
@Override @Override
public void addAvailableTriggeredMana(List<Mana> netManaAvailable public void addAvailableTriggeredMana(List<Mana> netManaAvailable
@ -3438,7 +3442,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/** /**
* @param ability * @param ability
* @param availableMana if null, it won't be checked if enough mana is * @param availableMana if null, it won't be checked if enough mana is
* available * available
* @param sourceObject * @param sourceObject
* @param game * @param game
* @return * @return
@ -3873,10 +3877,10 @@ public abstract class PlayerImpl implements Player, Serializable {
* currently cast/activate with his available resources * currently cast/activate with his available resources
* *
* @param game * @param game
* @param hidden also from hidden objects (e.g. turned face down cards ?) * @param hidden also from hidden objects (e.g. turned face down cards ?)
* @param fromZone of objects from which zone (ALL = from all zones) * @param fromZone of objects from which zone (ALL = from all zones)
* @param hideDuplicatedAbilities if equal abilities exist return only the * @param hideDuplicatedAbilities if equal abilities exist return only the
* first instance * first instance
* @return * @return
*/ */
public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) { public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
@ -4335,6 +4339,16 @@ public abstract class PlayerImpl implements Player, Serializable {
this.canPlayCardsFromGraveyard = playCardsFromGraveyard; this.canPlayCardsFromGraveyard = playCardsFromGraveyard;
} }
@Override
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
this.drawsOnOpponentsTurn = drawsOnOpponentsTurn;
}
@Override
public boolean isDrawsOnOpponentsTurn() {
return drawsOnOpponentsTurn;
}
@Override @Override
public boolean autoLoseGame() { public boolean autoLoseGame() {
return false; return false;
@ -4462,7 +4476,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override @Override
public boolean moveCards(Set<? extends Card> cards, Zone toZone, public boolean moveCards(Set<? extends Card> cards, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return moveCards(cards, toZone, source, game, false, false, false, null); return moveCards(cards, toZone, source, game, false, false, false, null);
} }
@ -4621,7 +4635,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// identify cards from one owner // identify cards from one owner
Cards cards = new CardsImpl(); Cards cards = new CardsImpl();
UUID ownerId = null; UUID ownerId = null;
for (Iterator<? extends Card> it = allCards.iterator(); it.hasNext();) { for (Iterator<? extends Card> it = allCards.iterator(); it.hasNext(); ) {
Card card = it.next(); Card card = it.next();
if (cards.isEmpty()) { if (cards.isEmpty()) {
ownerId = card.getOwnerId(); ownerId = card.getOwnerId();
@ -4799,7 +4813,7 @@ public abstract class PlayerImpl implements Player, Serializable {
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' ' + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
+ ' ' : "") + "to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId())); + ' ' : "") + "to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId()));
} }
} }