[AFR] Implemented Xanathar, Guild Kingpin (#8045)

* [AFR] Implemented Xanathar, Guild Kingpin

Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Raphael-Schulz 2021-08-28 21:55:41 +02:00 committed by GitHub
parent 65761b085f
commit f03a410b9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 477 additions and 55 deletions

View file

@ -9,6 +9,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import java.util.UUID;
@ -36,7 +37,7 @@ public final class ConspicuousSnoop extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may cast Goblin spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// 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"))));

View file

@ -10,6 +10,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterLandCard;
@ -34,7 +35,7 @@ public final class CourserOfKruphix extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may play lands from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// Whenever a land enters the battlefield under your control, you gain 1 life.
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GainLifeEffect(1), StaticFilters.FILTER_LAND_A));

View file

@ -9,10 +9,7 @@ import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
import mage.abilities.keyword.ProwessAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.Predicates;
@ -29,7 +26,7 @@ public final class ElshaOfTheInfinite extends CardImpl {
static {
filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
filter.add(CardOnTopOfLibraryPredicate.instance);
filter.add(CardOnTopOfLibraryPredicate.YOUR);
}
public ElshaOfTheInfinite(UUID ownerId, CardSetInfo setInfo) {
@ -48,7 +45,7 @@ public final class ElshaOfTheInfinite extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// 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, false));
Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false));
ability.addEffect(new CastAsThoughItHadFlashAllEffect(
Duration.WhileOnBattlefield, filter
).setText("If you cast a spell this way, you may cast it as though it had flash."));

View file

@ -55,7 +55,7 @@ public final class GaleaKindlerOfHope extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast Aura and Equipment spells from the top of your library. When you cast an Equipment spell this way, it gains "When this Equipment enters the battlefield, attach it to target creature you control."
Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false));
Ability ability = new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false));
ability.addEffect(new InfoEffect("When you cast an Equipment spell this way, it gains " +
"\"When this Equipment enters the battlefield, attach it to target creature you control.\""));
this.addAbility(ability);

View file

@ -9,6 +9,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
@ -35,7 +36,7 @@ public final class GarruksHorde extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
}
private GarruksHorde(final GarruksHorde card) {

View file

@ -9,10 +9,7 @@ import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.game.Game;
@ -49,7 +46,7 @@ public final class MelekIzzetParagon extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect()));
// You may cast instant and sorcery spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// 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());

View file

@ -12,6 +12,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterNonlandCard;
@ -43,7 +44,7 @@ public final class MysticForge extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast artifact spells and colorless spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// {T}, Pay 1 life: Exile the top card of your library.
Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost());

View file

@ -10,6 +10,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.filter.common.FilterLandCard;
@ -39,7 +40,7 @@ public final class OracleOfMulDaya extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
// You may play lands from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
}
private OracleOfMulDaya(final OracleOfMulDaya card) {

View file

@ -12,6 +12,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
@ -41,7 +42,7 @@ public final class PrecognitionField extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast instant and sorcery spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// {3}: Exile the top card of your library.
this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3)));

View file

@ -16,10 +16,7 @@ import mage.abilities.hint.common.MyTurnHint;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterLandCard;
@ -51,7 +48,7 @@ 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.
LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect();
lookEffect.setText("You may look at the top card of your library any time");
PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter, false);
PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(TargetController.YOU, filter, false);
playEffect.setText(", and you may play lands from the top of your library");
SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect);

View file

@ -15,6 +15,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
@ -61,7 +62,7 @@ public final class RangerClass extends CardImpl {
// You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(
new PlayTheTopCardEffect(filter, false), 3
new PlayTheTopCardEffect(TargetController.YOU, filter, false), 3
)));
}

View file

@ -12,6 +12,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
@ -45,7 +46,7 @@ public final class Realmwalker extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells of the chosen type from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
}
private Realmwalker(final Realmwalker card) {

View file

@ -10,6 +10,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
@ -58,7 +59,7 @@ class VergeRangersEffect extends PlayTheTopCardEffect {
private static final FilterCard filter = new FilterLandCard("play lands");
public VergeRangersEffect() {
super(filter, false);
super(TargetController.YOU, filter, false);
staticText = "As long as an opponent controls more lands than you, you may play lands from the top of your library";
}

View file

@ -52,7 +52,7 @@ public final class VivienMonstersAdvocate extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// +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.

View file

@ -37,7 +37,7 @@ public final class VizierOfTheMenagerie extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells from the top of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(filter, false)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// You may spend mana as though it were mana of any type to cast creature spells.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VizierOfTheMenagerieManaEffect()));

View file

@ -0,0 +1,185 @@
package mage.cards.x;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeTargetEffect;
import mage.abilities.effects.common.continuous.PlayTheTopCardTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
* @author raphael-schulz
*/
public final class XanatharGuildKingpin extends CardImpl {
public XanatharGuildKingpin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{B}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.BEHOLDER);
this.power = new MageInt(5);
this.toughness = new MageInt(6);
// At the beginning of your upkeep, choose target opponent. Until end of turn, that player cant cast spells, you may look at the top card of their library any time, you may play the top card of their library, and you may spend mana as though it were mana of any color to cast spells this way.
Ability ability = new BeginningOfUpkeepTriggeredAbility(
Zone.BATTLEFIELD, new XanatharGuildKingpinRuleModifyingEffect()
.setText("choose target opponent. Until end of turn, that player can't cast spells,"),
TargetController.YOU, false
);
ability.addEffect(new LookAtTopCardOfLibraryAnyTimeTargetEffect(Duration.EndOfTurn)
.setText(" you may look at the top card of their library any time,"));
ability.addEffect(new PlayTheTopCardTargetEffect()
.setText(" you may play the top card of their library,"));
ability.addEffect(new XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect()
.setText(" and you may spend mana as thought it were mana of any color to cast spells this way"));
ability.addCustomOutcome(Outcome.PreventCast);
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
}
private XanatharGuildKingpin(final XanatharGuildKingpin card) {
super(card);
}
@Override
public XanatharGuildKingpin copy() {
return new XanatharGuildKingpin(this);
}
}
class XanatharGuildKingpinRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl {
public XanatharGuildKingpinRuleModifyingEffect() {
super(Duration.EndOfTurn, Outcome.Benefit);
}
private XanatharGuildKingpinRuleModifyingEffect(final XanatharGuildKingpinRuleModifyingEffect effect) {
super(effect);
}
@Override
public XanatharGuildKingpinRuleModifyingEffect copy() {
return new XanatharGuildKingpinRuleModifyingEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
MageObject mageObject = game.getObject(source.getSourceId());
if (targetPlayer != null && mageObject != null) {
return "This turn you can't cast spells" +
" (" + mageObject.getLogName() + ')';
}
return null;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(getTargetPointer().getFirst(game, source));
}
}
class XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect extends OneShotEffect {
public XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect() {
super(Outcome.Benefit);
}
private XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect(final XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Card topCard = game.getPlayer(source.getFirstTarget()).getLibrary().getFromTop(game);
if (topCard == null) {
return false;
}
int zcc = game.getState().getZoneChangeCounter(topCard.getId());
game.addEffect(new SpendManaAsAnyColorToCastTopOfLibraryTargetEffect().setTargetPointer(new FixedTarget(topCard.getId(), zcc)), source);
return true;
}
@Override
public XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect copy() {
return new XanatharGuildKingpinSpendManaAsAnyColorOneShotEffect(this);
}
}
class SpendManaAsAnyColorToCastTopOfLibraryTargetEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public SpendManaAsAnyColorToCastTopOfLibraryTargetEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
}
public SpendManaAsAnyColorToCastTopOfLibraryTargetEffect(final SpendManaAsAnyColorToCastTopOfLibraryTargetEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public SpendManaAsAnyColorToCastTopOfLibraryTargetEffect copy() {
return new SpendManaAsAnyColorToCastTopOfLibraryTargetEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
UUID targetId = CardUtil.getMainCardId(game, fixedTarget.getTarget());
Card topCard = game.getPlayer(source.getFirstTarget()).getLibrary().getFromTop(game);
if (topCard == null) {
return false;
}
// If top card of target opponent's library changed, discard the current ContinuousEffect and create a new one
if (!topCard.getId().equals(targetId) && canLookAtNextTopLibraryCard(game) && !this.isDiscarded()) {
int zcc = game.getState().getZoneChangeCounter(topCard.getId());
game.addEffect(new SpendManaAsAnyColorToCastTopOfLibraryTargetEffect().setTargetPointer(new FixedTarget(topCard.getId(), zcc)), source);
this.discard();
}
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, targetId)
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.LIBRARY);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -281,6 +281,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
cards.add(new SetCardInfo("Wish", 166, Rarity.RARE, mage.cards.w.Wish.class));
cards.add(new SetCardInfo("Wizard Class", 81, Rarity.UNCOMMON, mage.cards.w.WizardClass.class));
cards.add(new SetCardInfo("Wizard's Spellbook", 82, Rarity.RARE, mage.cards.w.WizardsSpellbook.class));
cards.add(new SetCardInfo("Xanathar, Guild Kingpin", 239, Rarity.MYTHIC, mage.cards.x.XanatharGuildKingpin.class));
cards.add(new SetCardInfo("Xorn", 167, Rarity.RARE, mage.cards.x.Xorn.class));
cards.add(new SetCardInfo("You Come to a River", 83, Rarity.COMMON, mage.cards.y.YouComeToARiver.class));
cards.add(new SetCardInfo("You Come to the Gnoll Camp", 168, Rarity.COMMON, mage.cards.y.YouComeToTheGnollCamp.class));

View file

@ -0,0 +1,61 @@
package org.mage.test.cards.single.afr;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class XanatharGuildKingpinTest extends CardTestPlayerBase {
@Test
public void test_Play() {
removeAllCardsFromLibrary(playerA);
removeAllCardsFromLibrary(playerB);
skipInitShuffling();
// At the beginning of your upkeep, choose target opponent.
// Until end of turn, that player cant cast spells, you may look at the top card of their library any time,
// you may play the top card of their library, and you may spend mana as though it were mana of any color
// to cast spells this way.
addCard(Zone.BATTLEFIELD, playerA, "Xanathar, Guild Kingpin");
//
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
//
addCard(Zone.HAND, playerA, "Thunderbolt");
//
addCard(Zone.HAND, playerB, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
//
addCard(Zone.LIBRARY, playerB, "Mountain");
addCard(Zone.LIBRARY, playerB, "Lightning Bolt");
addCard(Zone.LIBRARY, playerB, "Grizzly Bears");
// activate on opponent
addTarget(playerA, playerB);
// B can't cast spells
checkPlayableAbility("B can't cast", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Lightning Bolt", false);
// A can cast own and from B library
checkPlayableAbility("A can cast own", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Thunderbolt", true);
checkPlayableAbility("A can cast from B", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
// cast from B and try another one with any color and full stack
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2); // pay for {G} as any
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", null, "Grizzly Bears");
addTarget(playerA, playerB); // bolt
checkStackSize("multi cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
// B can cast again
checkPlayableAbility("B can cast", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Lightning Bolt", true);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -4,25 +4,51 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public class LookAtTopCardOfLibraryAnyTimeEffect extends ContinuousEffectImpl {
private final TargetController targetLibrary;
public LookAtTopCardOfLibraryAnyTimeEffect() {
super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
staticText = "You may look at the top card of your library any time.";
this(TargetController.YOU, Duration.WhileOnBattlefield);
}
private LookAtTopCardOfLibraryAnyTimeEffect(final LookAtTopCardOfLibraryAnyTimeEffect effect) {
public LookAtTopCardOfLibraryAnyTimeEffect(TargetController targetLibrary, Duration duration) {
super(duration, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
this.targetLibrary = targetLibrary;
String libInfo;
switch (this.targetLibrary) {
case YOU:
libInfo = "your library";
break;
case OPPONENT:
libInfo = "opponents libraries";
break;
case SOURCE_TARGETS:
libInfo = "target player's library";
break;
default:
throw new IllegalArgumentException("Unknown target library type: " + targetLibrary);
}
staticText = duration.toString().isEmpty() ? "" : duration + " you may look at the top card of " + libInfo + " any time.";
}
protected LookAtTopCardOfLibraryAnyTimeEffect(final LookAtTopCardOfLibraryAnyTimeEffect effect) {
super(effect);
this.targetLibrary = effect.targetLibrary;
}
@Override
@ -34,18 +60,46 @@ public class LookAtTopCardOfLibraryAnyTimeEffect extends ContinuousEffectImpl {
if (controller == null) {
return false;
}
Card topCard = controller.getLibrary().getFromTop(game);
if (topCard == null) {
if (!canLookAtNextTopLibraryCard(game)) {
return false;
}
MageObject obj = source.getSourceObject(game);
if (obj == null) {
return false;
}
if (!canLookAtNextTopLibraryCard(game)) {
Set<UUID> needPlayers = new HashSet<>();
switch (this.targetLibrary) {
case YOU: {
needPlayers.add(source.getControllerId());
break;
}
case OPPONENT: {
needPlayers.addAll(game.getOpponents(source.getControllerId()));
break;
}
case SOURCE_TARGETS: {
needPlayers.addAll(CardUtil.getAllSelectedTargets(source, game));
break;
}
}
Set<Card> needCards = new HashSet<>();
needPlayers.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.map(player -> player.getLibrary().getFromTop(game))
.filter(Objects::nonNull)
.forEach(needCards::add);
if (needCards.isEmpty()) {
return false;
}
controller.lookAtCards("Top card of " + obj.getIdName() + " controller's library", topCard, game);
// all fine, can show top card
needCards.forEach(topCard -> {
Player owner = game.getPlayer(topCard.getOwnerId());
controller.lookAtCards(String.format("%s: top card of %s", obj.getName(), owner == null ? "error" : owner.getName()), topCard, game);
});
return true;
}

View file

@ -0,0 +1,23 @@
package mage.abilities.effects.common.continuous;
import mage.constants.Duration;
import mage.constants.TargetController;
/**
* @author JayDi85
*/
public class LookAtTopCardOfLibraryAnyTimeTargetEffect extends LookAtTopCardOfLibraryAnyTimeEffect {
public LookAtTopCardOfLibraryAnyTimeTargetEffect(Duration duration) {
super(TargetController.SOURCE_TARGETS, duration);
}
private LookAtTopCardOfLibraryAnyTimeTargetEffect(final LookAtTopCardOfLibraryAnyTimeTargetEffect effect) {
super(effect);
}
@Override
public LookAtTopCardOfLibraryAnyTimeTargetEffect copy() {
return new LookAtTopCardOfLibraryAnyTimeTargetEffect(this);
}
}

View file

@ -6,11 +6,14 @@ import mage.cards.Card;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
/**
@ -19,20 +22,44 @@ import java.util.UUID;
public class PlayTheTopCardEffect extends AsThoughEffectImpl {
private final FilterCard filter;
private final TargetController targetLibrary;
// can play card or can play lands/cast spells, see two modes below
private final boolean canPlayCardOnly;
/**
* Support targets, use TargetController.SOURCE_TARGETS
*/
public PlayTheTopCardEffect() {
this(new FilterCard("play lands and cast spells"), false);
this(TargetController.YOU);
}
public PlayTheTopCardEffect(FilterCard filter, boolean canPlayCardOnly) {
public PlayTheTopCardEffect(TargetController targetLibrary) {
this(targetLibrary, new FilterCard("play lands and cast spells"), false);
}
public PlayTheTopCardEffect(TargetController targetLibrary, FilterCard filter, boolean canPlayCardOnly) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter;
this.targetLibrary = targetLibrary;
this.canPlayCardOnly = canPlayCardOnly;
this.staticText = "You may " + filter.getMessage() + " from the top of your library";
String libInfo;
switch (this.targetLibrary) {
case YOU:
libInfo = "your library";
break;
case OPPONENT:
libInfo = "opponents libraries";
break;
case SOURCE_TARGETS:
libInfo = "target player's library";
break;
default:
throw new IllegalArgumentException("Unknown target library type: " + targetLibrary);
}
this.staticText = "You may " + filter.getMessage() + " from the top of " + libInfo;
// 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)
@ -44,6 +71,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) {
super(effect);
this.filter = effect.filter;
this.targetLibrary = effect.targetLibrary;
this.canPlayCardOnly = effect.canPlayCardOnly;
}
@ -71,7 +99,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
}
if (this.canPlayCardOnly) {
// check whole card intead part
// check whole card instead part
cardToCheck = cardToCheck.getMainCard();
}
@ -80,17 +108,51 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
return false;
}
// must be your card
Player player = game.getPlayer(cardToCheck.getOwnerId());
if (player == null || !player.getId().equals(affectedControllerId)) {
Player cardOwner = game.getPlayer(cardToCheck.getOwnerId());
Player controller = game.getPlayer(source.getControllerId());
if (cardOwner == null || controller == null) {
return false;
}
// must be from your library
Card topCard = player.getLibrary().getFromTop(game);
// must be your or opponents library
switch (this.targetLibrary) {
case YOU: {
Card topCard = controller.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
break;
}
case OPPONENT: {
if (!game.getOpponents(controller.getId()).contains(cardOwner.getId())) {
return false;
}
Card topCard = cardOwner.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
break;
}
case SOURCE_TARGETS: {
UUID needCardId = cardToCheck.getMainCard().getId();
if (CardUtil.getAllSelectedTargets(source, game).stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.noneMatch(player -> {
Card topCard = player.getLibrary().getFromTop(game);
return topCard != null && topCard.getId().equals(needCardId);
})) {
return false;
}
break;
}
default: {
return false;
}
}
// can't cast without mana cost
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {

View file

@ -0,0 +1,22 @@
package mage.abilities.effects.common.continuous;
import mage.constants.TargetController;
/**
* @author JayDi85
*/
public class PlayTheTopCardTargetEffect extends PlayTheTopCardEffect {
public PlayTheTopCardTargetEffect() {
super(TargetController.SOURCE_TARGETS);
}
public PlayTheTopCardTargetEffect(final PlayTheTopCardTargetEffect effect) {
super(effect);
}
@Override
public PlayTheTopCardTargetEffect copy() {
return new PlayTheTopCardTargetEffect(this);
}
}

View file

@ -28,7 +28,7 @@ public enum Outcome {
PutManaInPool(true),
Regenerate(true),
PreventDamage(true), // TODO: check good or bad
PreventCast(false), // TODO: check good or bad
PreventCast(false),
RedirectDamage(true), // TODO: check good or bad
Tap(false),
Transform(true),

View file

@ -25,7 +25,8 @@ public enum TargetController {
OWNER,
CONTROLLER_ATTACHED_TO,
NEXT,
EACH_PLAYER;
EACH_PLAYER,
SOURCE_TARGETS;
private final OwnerPredicate ownerPredicate;
private final PlayerPredicate playerPredicate;

View file

@ -11,11 +11,24 @@ import mage.players.Player;
*/
public enum CardOnTopOfLibraryPredicate implements ObjectPlayerPredicate<ObjectPlayer<Card>> {
instance;
YOUR,
ANY;
@Override
public boolean apply(ObjectPlayer<Card> input, Game game) {
Player player = game.getPlayer(input.getObject().getOwnerId());
Player player;
switch (this) {
case YOUR:
player = game.getPlayer(input.getPlayerId());
break;
case ANY:
default:
player = game.getPlayer(input.getObject().getOwnerId());
break;
}
if (player == null) {
return false;
}