refactor and test 'Scry X' effects

Fixes #13667
This commit is contained in:
Susucre 2025-05-25 17:50:07 +02:00
parent 04b5660d17
commit 0b6a2b2546
21 changed files with 505 additions and 407 deletions

View file

@ -4,22 +4,24 @@ import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.Hint; import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint; import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.common.FilterControlledArtifactPermanent;
import mage.filter.predicate.permanent.TappedPredicate; import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetAnyTarget; import mage.target.common.TargetAnyTarget;
import java.util.UUID; import java.util.UUID;
@ -29,6 +31,15 @@ import java.util.UUID;
*/ */
public final class AlibouAncientWitness extends CardImpl { public final class AlibouAncientWitness extends CardImpl {
private static final FilterPermanent filter = new FilterControlledArtifactPermanent("tapped artifacts you control");
static {
filter.add(TappedPredicate.TAPPED);
}
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
private static final Hint hint = new ValueHint("Tapped artifacts you control", xValue);
public AlibouAncientWitness(UUID ownerId, CardSetInfo setInfo) { public AlibouAncientWitness(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}");
@ -45,11 +56,12 @@ public final class AlibouAncientWitness extends CardImpl {
// Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control. // Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control.
Ability ability = new AttacksWithCreaturesTriggeredAbility( Ability ability = new AttacksWithCreaturesTriggeredAbility(
new AlibouAncientWitnessEffect(), 1, new DamageTargetEffect(xValue).setText("{this} deals X damage to any target"),
StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE 1, StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE
).setTriggerPhrase("Whenever one or more artifact creatures you control attack, "); ).setTriggerPhrase("Whenever one or more artifact creatures you control attack, ");
ability.addEffect(new ScryEffect(xValue).concatBy("and you"));
ability.addTarget(new TargetAnyTarget()); ability.addTarget(new TargetAnyTarget());
this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint())); this.addAbility(ability.addHint(hint));
} }
private AlibouAncientWitness(final AlibouAncientWitness card) { private AlibouAncientWitness(final AlibouAncientWitness card) {
@ -60,57 +72,4 @@ public final class AlibouAncientWitness extends CardImpl {
public AlibouAncientWitness copy() { public AlibouAncientWitness copy() {
return new AlibouAncientWitness(this); return new AlibouAncientWitness(this);
} }
} }
class AlibouAncientWitnessEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterControlledArtifactPermanent();
static {
filter.add(TappedPredicate.TAPPED);
}
private static final Hint hint = new ValueHint(
"Tapped artifacts you control", new PermanentsOnBattlefieldCount(filter)
);
AlibouAncientWitnessEffect() {
super(Outcome.Benefit);
staticText = "{this} deals X damage to any target and you scry X, " +
"where X is the number of tapped artifacts you control";
}
private AlibouAncientWitnessEffect(final AlibouAncientWitnessEffect effect) {
super(effect);
}
@Override
public AlibouAncientWitnessEffect copy() {
return new AlibouAncientWitnessEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int xValue = game.getBattlefield().count(filter, source.getControllerId(), source, game);
if (xValue < 1) {
return false;
}
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) {
permanent.damage(xValue, source.getSourceId(), source, game);
}
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
player.damage(xValue, source.getSourceId(), source, game);
}
player = game.getPlayer(source.getControllerId());
if (player != null) {
player.scry(xValue, source, game);
}
return true;
}
public static Hint getHint() {
return hint;
}
}

View file

@ -8,6 +8,7 @@ import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.dynamicvalue.common.GetXValue;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -44,7 +45,7 @@ public final class BrainInAJar extends CardImpl {
this.addAbility(ability); this.addAbility(ability);
// {3}, {T}, Remove X charge counters from Brain in a Jar: Scry X. // {3}, {T}, Remove X charge counters from Brain in a Jar: Scry X.
ability = new SimpleActivatedAbility(new BrainInAJarScryEffect(), new GenericManaCost(3)); ability = new SimpleActivatedAbility(new ScryEffect(GetXValue.instance), new GenericManaCost(3));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE));
this.addAbility(ability); this.addAbility(ability);
@ -90,34 +91,4 @@ class BrainInAJarCastEffect extends OneShotEffect {
filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters)); filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters));
return CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter); return CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter);
} }
} }
class BrainInAJarScryEffect extends OneShotEffect {
BrainInAJarScryEffect() {
super(Outcome.Benefit);
this.staticText = "Scry X";
}
private BrainInAJarScryEffect(final BrainInAJarScryEffect effect) {
super(effect);
}
@Override
public BrainInAJarScryEffect copy() {
return new BrainInAJarScryEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int x = GetXValue.instance.calculate(game, source, this);
if (x > 0) {
return controller.scry(x, source, game);
}
return true;
}
return false;
}
}

View file

@ -1,18 +1,14 @@
package mage.cards.c; package mage.cards.c;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.PartyCount; import mage.abilities.dynamicvalue.common.PartyCount;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.common.PartyCountHint; import mage.abilities.hint.common.PartyCountHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID; import java.util.UUID;
@ -20,7 +16,6 @@ import java.util.UUID;
* @author TheElk801 * @author TheElk801
*/ */
public final class CascadeSeer extends CardImpl { public final class CascadeSeer extends CardImpl {
public CascadeSeer(UUID ownerId, CardSetInfo setInfo) { public CascadeSeer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
@ -30,9 +25,14 @@ public final class CascadeSeer extends CardImpl {
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// When Cascade Seer enters the battlefield, scry X, where X is the number of creatures in your party. // When Cascade Seer enters the battlefield, scry X, where X is the number of creatures in your party.
this.addAbility(new EntersBattlefieldTriggeredAbility(new CascadeSeerEffect()).addHint(PartyCountHint.instance)); this.addAbility(
new EntersBattlefieldTriggeredAbility(
new ScryEffect(PartyCount.instance)
.setText("scry X, where X is the number of creatures in your party")
).addHint(PartyCountHint.instance));
} }
private CascadeSeer(final CascadeSeer card) { private CascadeSeer(final CascadeSeer card) {
super(card); super(card);
} }
@ -41,31 +41,4 @@ public final class CascadeSeer extends CardImpl {
public CascadeSeer copy() { public CascadeSeer copy() {
return new CascadeSeer(this); return new CascadeSeer(this);
} }
} }
class CascadeSeerEffect extends OneShotEffect {
CascadeSeerEffect() {
super(Outcome.Benefit);
staticText = "scry X, where X is the number of creatures in your party. " + PartyCount.getReminder();
}
private CascadeSeerEffect(final CascadeSeerEffect effect) {
super(effect);
}
@Override
public CascadeSeerEffect copy() {
return new CascadeSeerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
int partyCount = PartyCount.instance.calculate(game, source, this);
return partyCount > 0 && player.scry(partyCount, source, game);
}
}

View file

@ -6,6 +6,7 @@ import mage.abilities.dynamicvalue.common.GetXValue;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.RollDieWithResultTableEffect; import mage.abilities.effects.common.RollDieWithResultTableEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -35,7 +36,10 @@ public final class DivinersPortent extends CardImpl {
effect.addTableEntry(1, 14, new DrawCardSourceControllerEffect(GetXValue.instance)); effect.addTableEntry(1, 14, new DrawCardSourceControllerEffect(GetXValue.instance));
// 15+ | Scry X, then draw X cards. // 15+ | Scry X, then draw X cards.
effect.addTableEntry(15, Integer.MAX_VALUE, new DivinersPortentEffect()); effect.addTableEntry(15, Integer.MAX_VALUE,
new ScryEffect(GetXValue.instance),
new DrawCardSourceControllerEffect(GetXValue.instance).concatBy(", then")
);
} }
private DivinersPortent(final DivinersPortent card) { private DivinersPortent(final DivinersPortent card) {

View file

@ -5,6 +5,7 @@ import mage.abilities.Ability;
import mage.abilities.common.FinishVotingTriggeredAbility; import mage.abilities.common.FinishVotingTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -13,7 +14,6 @@ import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.token.TreasureToken; import mage.game.permanent.token.TreasureToken;
import mage.players.Player;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -68,19 +68,16 @@ class ErestorOfTheCouncilEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Set<UUID> playerIds = (Set<UUID>) getValue("votedAgainst"); Set<UUID> playerIds = (Set<UUID>) getValue("votedAgainst");
int count = 0; int scryCount = 0;
for (UUID opponentId : game.getOpponents(source.getControllerId())) { for (UUID opponentId : game.getOpponents(source.getControllerId())) {
if (playerIds.contains(opponentId)) { if (playerIds.contains(opponentId)) {
count++; scryCount++;
} else { } else {
new TreasureToken().putOntoBattlefield(1, game, source, opponentId); new TreasureToken().putOntoBattlefield(1, game, source, opponentId);
} }
} }
if (count > 0) { if (scryCount > 0) {
Player player = game.getPlayer(source.getControllerId()); new ScryEffect(scryCount).apply(game, source);
if (player != null) {
player.scry(count, source, game);
}
} }
return true; return true;
} }

View file

@ -7,18 +7,17 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint; import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.game.Game; import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.watchers.common.CastSpellLastTurnWatcher; import mage.watchers.common.CastSpellLastTurnWatcher;
@ -29,6 +28,8 @@ import java.util.UUID;
*/ */
public final class GnostroVoiceOfTheCrags extends CardImpl { public final class GnostroVoiceOfTheCrags extends CardImpl {
private static final Hint hint = new ValueHint("Number of spells you've cast this turn", GnostroVoiceOfTheCragsValue.instance);
public GnostroVoiceOfTheCrags(UUID ownerId, CardSetInfo setInfo) { public GnostroVoiceOfTheCrags(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}");
@ -39,8 +40,8 @@ public final class GnostroVoiceOfTheCrags extends CardImpl {
// {T}: Choose one. X is the number of spells you've cast this turn. // {T}: Choose one. X is the number of spells you've cast this turn.
// Scry X. // Scry X.
Ability ability = new SimpleActivatedAbility(new GnostroVoiceOfTheCragsEffect(), new TapSourceCost()); Ability ability = new SimpleActivatedAbility(new ScryEffect(GnostroVoiceOfTheCragsValue.instance), new TapSourceCost());
ability.addHint(new ValueHint("Number of spells you've cast this turn", GnostroVoiceOfTheCragsValue.instance)); ability.addHint(hint);
ability.getModes().setChooseText("choose one. X is the number of spells you've cast this turn."); ability.getModes().setChooseText("choose one. X is the number of spells you've cast this turn.");
// Gnostro, Voice of the Crags deals X damage to target creature. // Gnostro, Voice of the Crags deals X damage to target creature.
@ -78,36 +79,12 @@ enum GnostroVoiceOfTheCragsValue implements DynamicValue {
return instance; return instance;
} }
public String toString() {
return "X";
}
@Override @Override
public String getMessage() { public String getMessage() {
return ""; return "";
} }
} }
class GnostroVoiceOfTheCragsEffect extends OneShotEffect {
GnostroVoiceOfTheCragsEffect() {
super(Outcome.Benefit);
staticText = "scry X";
}
private GnostroVoiceOfTheCragsEffect(final GnostroVoiceOfTheCragsEffect effect) {
super(effect);
}
@Override
public GnostroVoiceOfTheCragsEffect copy() {
return new GnostroVoiceOfTheCragsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
return player.scry(
GnostroVoiceOfTheCragsValue.instance.calculate(game, source, this), source, game
);
}
}

View file

@ -2,6 +2,8 @@ package mage.cards.g;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -60,10 +62,8 @@ class GravenLoreEffect extends OneShotEffect {
return false; return false;
} }
int snow = ManaPaidSourceWatcher.getSnowPaid(source.getId(), game); int snow = ManaPaidSourceWatcher.getSnowPaid(source.getId(), game);
if (snow > 0) { boolean result = new ScryEffect(snow).apply(game, source);
player.scry(snow, source, game); result |= new DrawCardSourceControllerEffect(3).apply(game, source);
} return result;
player.drawCards(3, source, game);
return true;
} }
} }

View file

@ -2,6 +2,7 @@ package mage.cards.n;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@ -67,7 +68,9 @@ class NetheresePuzzleWardEffect extends OneShotEffect {
if (player == null) { if (player == null) {
return false; return false;
} }
return player.scry(player.rollDice(outcome, source, game, 4), source, game); int roll = player.rollDice(outcome, source, game, 4);
new ScryEffect(roll).apply(game, source);
return true;
} }
} }

View file

@ -1,27 +1,32 @@
package mage.cards.o; package mage.cards.o;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.filter.StaticFilters; import mage.filter.FilterPermanent;
import mage.game.Game; import mage.filter.common.FilterControlledPlaneswalkerPermanent;
import mage.players.Player;
import java.util.UUID;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public final class OathOfJace extends CardImpl { public final class OathOfJace extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPlaneswalkerPermanent("planeswalkers you control");
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
private static final Hint hint = new ValueHint("Planeswalkers you control", xValue);
public OathOfJace(UUID ownerId, CardSetInfo setInfo) { public OathOfJace(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
this.supertype.add(SuperType.LEGENDARY); this.supertype.add(SuperType.LEGENDARY);
@ -30,8 +35,7 @@ public final class OathOfJace extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(3, 2), false)); this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(3, 2), false));
// At the beginning of your upkeep, scry X, where X is the number of planeswalkers you control. // At the beginning of your upkeep, scry X, where X is the number of planeswalkers you control.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new OathOfJaceEffect())); this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ScryEffect(xValue)).addHint(hint));
} }
private OathOfJace(final OathOfJace card) { private OathOfJace(final OathOfJace card) {
@ -42,34 +46,4 @@ public final class OathOfJace extends CardImpl {
public OathOfJace copy() { public OathOfJace copy() {
return new OathOfJace(this); return new OathOfJace(this);
} }
} }
class OathOfJaceEffect extends OneShotEffect {
OathOfJaceEffect() {
super(Outcome.DrawCard);
this.staticText = "scry X, where X is the number of planeswalkers you control";
}
private OathOfJaceEffect(final OathOfJaceEffect effect) {
super(effect);
}
@Override
public OathOfJaceEffect copy() {
return new OathOfJaceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int planeswalker = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_PLANESWALKER, source.getControllerId(), game);
if (planeswalker > 0) {
controller.scry(planeswalker, source, game);
}
return true;
}
return false;
}
}

View file

@ -1,23 +1,23 @@
package mage.cards.s; package mage.cards.s;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValuePositiveHint;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.PartnerAbility; import mage.abilities.keyword.PartnerAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AbilityPredicate; import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.AttackingPredicate;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID; import java.util.UUID;
@ -26,6 +26,16 @@ import java.util.UUID;
*/ */
public final class SianiEyeOfTheStorm extends CardImpl { public final class SianiEyeOfTheStorm extends CardImpl {
private static final FilterPermanent filter = new FilterCreaturePermanent("attacking creatures with flying");
static {
filter.add(new AbilityPredicate(FlyingAbility.class));
filter.add(AttackingPredicate.instance);
}
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
private static final Hint hint = new ValuePositiveHint("Number of Rats you control", xValue);
public SianiEyeOfTheStorm(UUID ownerId, CardSetInfo setInfo) { public SianiEyeOfTheStorm(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
@ -39,7 +49,7 @@ public final class SianiEyeOfTheStorm extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Whenever Siani, Eye of the Storm attacks, scry X, where X is the number of attacking creatures with flying. // Whenever Siani, Eye of the Storm attacks, scry X, where X is the number of attacking creatures with flying.
this.addAbility(new AttacksTriggeredAbility(new SianiEyeOfTheStormEffect(), false)); this.addAbility(new AttacksTriggeredAbility(new ScryEffect(xValue)).addHint(hint));
// Partner // Partner
this.addAbility(PartnerAbility.getInstance()); this.addAbility(PartnerAbility.getInstance());
@ -53,38 +63,4 @@ public final class SianiEyeOfTheStorm extends CardImpl {
public SianiEyeOfTheStorm copy() { public SianiEyeOfTheStorm copy() {
return new SianiEyeOfTheStorm(this); return new SianiEyeOfTheStorm(this);
} }
} }
class SianiEyeOfTheStormEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterCreaturePermanent();
static {
filter.add(new AbilityPredicate(FlyingAbility.class));
filter.add(AttackingPredicate.instance);
}
SianiEyeOfTheStormEffect() {
super(Outcome.Benefit);
staticText = "scry X, where X is the number of attacking creatures with flying";
}
private SianiEyeOfTheStormEffect(final SianiEyeOfTheStormEffect effect) {
super(effect);
}
@Override
public SianiEyeOfTheStormEffect copy() {
return new SianiEyeOfTheStormEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
int count = game.getBattlefield().count(filter, source.getControllerId(), source, game);
return count > 0 && player.scry(count, source, game);
}
}

View file

@ -1,19 +1,15 @@
package mage.cards.s; package mage.cards.s;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.PrototypeAbility; import mage.abilities.keyword.PrototypeAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.UUID; import java.util.UUID;
@ -36,7 +32,9 @@ public final class SpotterThopter extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// When Spotter Thopter enters the battlefield, scry X, where X is its power. // When Spotter Thopter enters the battlefield, scry X, where X is its power.
this.addAbility(new EntersBattlefieldTriggeredAbility(new SpotterThopterEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(
new ScryEffect(SourcePermanentPowerValue.NOT_NEGATIVE)
.setText("scry X, where X is its power")));
} }
private SpotterThopter(final SpotterThopter card) { private SpotterThopter(final SpotterThopter card) {
@ -48,31 +46,3 @@ public final class SpotterThopter extends CardImpl {
return new SpotterThopter(this); return new SpotterThopter(this);
} }
} }
class SpotterThopterEffect extends OneShotEffect {
SpotterThopterEffect() {
super(Outcome.Benefit);
staticText = "scry X, where X is its power";
}
private SpotterThopterEffect(final SpotterThopterEffect effect) {
super(effect);
}
@Override
public SpotterThopterEffect copy() {
return new SpotterThopterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = source.getSourcePermanentOrLKI(game);
if (player == null || permanent == null) {
return false;
}
int power = permanent.getPower().getValue();
return power > 0 && player.scry(power, source, game);
}
}

View file

@ -4,6 +4,8 @@ import mage.MageInt;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility; import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
@ -31,7 +33,6 @@ import mage.target.targetpointer.FixedTarget;
import java.util.UUID; import java.util.UUID;
/** /**
*
* @author spjspj * @author spjspj
*/ */
public final class TheScarabGod extends CardImpl { public final class TheScarabGod extends CardImpl {
@ -51,15 +52,17 @@ public final class TheScarabGod extends CardImpl {
this.toughness = new MageInt(5); this.toughness = new MageInt(5);
// At the beginning of your upkeep, each opponent loses X life and you scry X, where X is the number of Zombies you control. // At the beginning of your upkeep, each opponent loses X life and you scry X, where X is the number of Zombies you control.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TheScarabGodEffect(xValue)).addHint(hint)); Ability ability = new BeginningOfUpkeepTriggeredAbility(new LoseLifeOpponentsEffect(xValue).setText("each opponent loses X life"));
ability.addEffect(new ScryEffect(xValue).concatBy("and you"));
this.addAbility(ability.addHint(hint));
// {2}{U}{B}: Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie. // {2}{U}{B}: Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie.
Ability ability = new SimpleActivatedAbility(new TheScarabGodEffect2(), new ManaCostsImpl<>("{2}{U}{B}")); ability = new SimpleActivatedAbility(new TheScarabGodExileEffect(), new ManaCostsImpl<>("{2}{U}{B}"));
ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD)); ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD));
this.addAbility(ability); this.addAbility(ability);
// When The Scarab God dies, return it to its owner's hand at the beginning of the next end step. // When The Scarab God dies, return it to its owner's hand at the beginning of the next end step.
this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffect3())); this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffectDieEffect()));
} }
private TheScarabGod(final TheScarabGod card) { private TheScarabGod(final TheScarabGod card) {
@ -72,61 +75,20 @@ public final class TheScarabGod extends CardImpl {
} }
} }
class TheScarabGodEffect extends OneShotEffect { class TheScarabGodExileEffect extends OneShotEffect {
private final DynamicValue numOfZombies; public TheScarabGodExileEffect() {
public TheScarabGodEffect(DynamicValue numOfZombies) {
super(Outcome.Benefit);
this.numOfZombies = numOfZombies;
staticText = "each opponent loses X life and you scry X, where X is the number of Zombies you control";
}
private TheScarabGodEffect(final TheScarabGodEffect effect) {
super(effect);
this.numOfZombies = effect.numOfZombies;
}
@Override
public TheScarabGodEffect copy() {
return new TheScarabGodEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int numZombies = numOfZombies.calculate(game, source, this);
if (numZombies > 0) {
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(playerId);
if (opponent != null) {
opponent.loseLife(numZombies, game, source, false);
}
}
controller.scry(numZombies, source, game);
}
return true;
}
return false;
}
}
class TheScarabGodEffect2 extends OneShotEffect {
public TheScarabGodEffect2() {
super(Outcome.PutCreatureInPlay); super(Outcome.PutCreatureInPlay);
this.staticText = "Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie."; this.staticText = "Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie.";
} }
private TheScarabGodEffect2(final TheScarabGodEffect2 effect) { private TheScarabGodExileEffect(final TheScarabGodExileEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public TheScarabGodEffect2 copy() { public TheScarabGodExileEffect copy() {
return new TheScarabGodEffect2(this); return new TheScarabGodExileEffect(this);
} }
@Override @Override
@ -147,16 +109,16 @@ class TheScarabGodEffect2 extends OneShotEffect {
} }
} }
class TheScarabGodEffect3 extends OneShotEffect { class TheScarabGodEffectDieEffect extends OneShotEffect {
private static final String effectText = "return it to its owner's hand at the beginning of the next end step"; private static final String effectText = "return it to its owner's hand at the beginning of the next end step";
TheScarabGodEffect3() { TheScarabGodEffectDieEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
staticText = effectText; staticText = effectText;
} }
private TheScarabGodEffect3(final TheScarabGodEffect3 effect) { private TheScarabGodEffectDieEffect(final TheScarabGodEffectDieEffect effect) {
super(effect); super(effect);
} }
@ -172,7 +134,7 @@ class TheScarabGodEffect3 extends OneShotEffect {
} }
@Override @Override
public TheScarabGodEffect3 copy() { public TheScarabGodEffectDieEffect copy() {
return new TheScarabGodEffect3(this); return new TheScarabGodEffectDieEffect(this);
} }
} }

View file

@ -2,15 +2,22 @@ package mage.cards.t;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SagaAbility; import mage.abilities.common.SagaAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.MillCardsControllerEffect; import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.token.ZombieToken; import mage.game.permanent.token.ZombieToken;
@ -25,6 +32,11 @@ import java.util.UUID;
*/ */
public final class TymaretCallsTheDead extends CardImpl { public final class TymaretCallsTheDead extends CardImpl {
private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.ZOMBIE, "Zombies you control");
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
private static final Hint hint = new ValueHint("Number of Rats you control", xValue);
public TymaretCallsTheDead(UUID ownerId, CardSetInfo setInfo) { public TymaretCallsTheDead(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
@ -39,7 +51,10 @@ public final class TymaretCallsTheDead extends CardImpl {
); );
// III You gain X life and scry X, where X is the number of Zombies you control. // III You gain X life and scry X, where X is the number of Zombies you control.
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new TymaretCallsTheDeadLastEffect()); sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III,
new GainLifeEffect(xValue).setText("You gain X life"),
new ScryEffect(xValue).concatBy("and"));
sagaAbility.addHint(hint);
this.addAbility(sagaAbility); this.addAbility(sagaAbility);
} }
@ -101,38 +116,4 @@ class TymaretCallsTheDeadFirstEffect extends OneShotEffect {
return player.moveCards(game.getCard(target.getFirstTarget()), Zone.EXILED, source, game) return player.moveCards(game.getCard(target.getFirstTarget()), Zone.EXILED, source, game)
&& tokenEffect.apply(game, source); && tokenEffect.apply(game, source);
} }
} }
class TymaretCallsTheDeadLastEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent(SubType.ZOMBIE, "");
TymaretCallsTheDeadLastEffect() {
super(Outcome.Benefit);
staticText = "You gain X life and scry X, where X is the number of Zombies you control.";
}
private TymaretCallsTheDeadLastEffect(final TymaretCallsTheDeadLastEffect effect) {
super(effect);
}
@Override
public TymaretCallsTheDeadLastEffect copy() {
return new TymaretCallsTheDeadLastEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
int zombieCount = game.getBattlefield().countAll(filter, source.getControllerId(), game);
if (zombieCount <= 0) {
return true;
}
player.gainLife(zombieCount, game, source);
player.scry(zombieCount, source, game);
return true;
}
}

View file

@ -0,0 +1,66 @@
package org.mage.test.cards.single.bro;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class SpotterThopterTest extends CardTestPlayerBase {
/**
* {@link mage.cards.s.SpotterThopter Spotter Thopter} {8}
* Artifact Creature Thopter
* Prototype {3}{U} 2/3 (You may cast this spell with different mana cost, color, and size. It keeps its abilities and types.)
* Flying
* When this creature enters, scry X, where X is its power.
* 4/5
*/
private static final String thopter = "Spotter Thopter";
@Test
public void test_prototype_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.LIBRARY, playerA, "Crucible of Worlds");
addCard(Zone.LIBRARY, playerA, "Desecration Demon");
addCard(Zone.HAND, playerA, thopter, 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thopter + " using Prototype");
// Oath of Jace's upkeep trigger triggers
addTarget(playerA, "Desecration Demon"); // put on bottom with scry 2
setStrictChooseMode(true);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
}
@Test
public void test_normal_scry4() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.LIBRARY, playerA, "Crucible of Worlds");
addCard(Zone.LIBRARY, playerA, "Desecration Demon");
addCard(Zone.HAND, playerA, thopter, 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 8);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thopter);
// Oath of Jace's upkeep trigger triggers
addTarget(playerA, "Desecration Demon^Crucible of Worlds"); // put on bottom with scry 2
setChoice(playerA, "Crucible of Worlds"); // order for bottom
setChoice(playerA, "Baleful Strix"); // order for top
setStrictChooseMode(true);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
}
}

View file

@ -0,0 +1,49 @@
package org.mage.test.cards.single.c21;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class AlibouAncientWitnessTest extends CardTestPlayerBase {
/**
* {@link mage.cards.a.AlibouAncientWitness Alibou, Ancient Witness {3}{R}{W}
* Legendary Artifact Creature Golem
* Other artifact creatures you control have haste.
* Whenever one or more artifact creatures you control attack, Alibou deals X damage to any target and you scry X, where X is the number of tapped artifacts you control.
* 4/5
*/
private static final String alibou = "Alibou, Ancient Witness";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.BATTLEFIELD, playerA, alibou);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.BATTLEFIELD, playerA, "Ornithopter");
addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker");
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
attack(1, playerA, alibou, playerB);
attack(1, playerA, "Memnite", playerB);
attack(1, playerA, "Goblin Piker", playerB);
attack(1, playerA, "Elite Vanguard", playerB);
addTarget(playerA, playerB); // Alibou's trigger
addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2
setChoice(playerA, "Ancient Amphitheater"); // order for bottom
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 20 - 4 - 1 - 2 - 2 - 2);
}
}

View file

@ -0,0 +1,49 @@
package org.mage.test.cards.single.cmr;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class SianiEyeOfTheStormTest extends CardTestPlayerBase {
/**
* {@link mage.cards.s.SianiEyeOfTheStorm Siani, Eye of the Storm} {3}{U}
* Legendary Creature Djinn Monk
* Flying
* Whenever Siani attacks, scry X, where X is the number of attacking creatures with flying.
* Partner (You can have two commanders if both have partner.)
* 3/2
*/
private static final String siani = "Siani, Eye of the Storm";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.BATTLEFIELD, playerA, siani);
addCard(Zone.BATTLEFIELD, playerA, "Air Elemental");
addCard(Zone.BATTLEFIELD, playerA, "Abbey Griffin");
addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker");
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
attack(1, playerA, siani, playerB);
attack(1, playerA, "Air Elemental", playerB);
attack(1, playerA, "Goblin Piker", playerB);
attack(1, playerA, "Elite Vanguard", playerB);
addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2
setChoice(playerA, "Ancient Amphitheater"); // order for bottom
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 20 - 3 - 4 - 2 - 2);
}
}

View file

@ -0,0 +1,41 @@
package org.mage.test.cards.single.hou;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class TheScarabGodTest extends CardTestPlayerBase {
/**
* {@link mage.cards.t.TheScarabGod The Scarab God} {3}{U}{B}
* Legendary Creature God
* At the beginning of your upkeep, each opponent loses X life and you scry X, where X is the number of Zombies you control.
* {2}{U}{B}: Exile target creature card from a graveyard. Create a token thats a copy of it, except its a 4/4 black Zombie.
* When The Scarab God dies, return it to its owners hand at the beginning of the next end step.
* 5/5
*/
private static final String god = "The Scarab God";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.BATTLEFIELD, playerA, god, 1);
addCard(Zone.BATTLEFIELD, playerA, "Legions of Lim-Dul", 2);
// The Scarab God's upkeep trigger triggers
addTarget(playerA, "Ancient Amphitheater"); // put on bottom with scry 2
setStrictChooseMode(true);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerB, 20 - 2);
}
}

View file

@ -0,0 +1,40 @@
package org.mage.test.cards.single.khm;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class GravenLoreTest extends CardTestPlayerBase {
/**
* {@link mage.cards.g.GravenLore Graven Lore} {3}{U}{U}
* Snow Instant
* Scry X, where X is the amount of {S} spent to cast this spell, then draw three cards. ({S} is mana from a snow source.)
*/
private static final String lore = "Graven Lore";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.HAND, playerA, lore, 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Island", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lore);
addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2
setChoice(playerA, "Ancient Amphitheater"); // order for bottom
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, "Mountain", 3);
}
}

View file

@ -0,0 +1,42 @@
package org.mage.test.cards.single.ogw;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class OathOfJaceTest extends CardTestPlayerBase {
/**
* {@link mage.cards.o.OathOfJace Oath of Jace} {2}{U}
* Legendary Enchantment
* When Oath of Jace enters, draw three cards, then discard two cards.
* At the beginning of your upkeep, scry X, where X is the number of planeswalkers you control.
*/
private static final String oath = "Oath of Jace";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
addCard(Zone.BATTLEFIELD, playerA, "Legions of Lim-Dul", 2); // not a planeswalker
addCard(Zone.BATTLEFIELD, playerA, "Chandra Nalaar");
addCard(Zone.BATTLEFIELD, playerA, "Jace Beleren");
addCard(Zone.BATTLEFIELD, playerB, "Ajani, Caller of the Pride"); // not in your control
// Oath of Jace's upkeep trigger triggers
addTarget(playerA, "Ancient Amphitheater^Baleful Strix"); // put on bottom with scry 2
setChoice(playerA, "Baleful Strix"); // ordering the bottom
setStrictChooseMode(true);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
}
}

View file

@ -0,0 +1,49 @@
package org.mage.test.cards.single.sir;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class BrainInAJarTest extends CardTestPlayerBase {
/**
* {@link mage.cards.b.BrainInAJar Brain in a Jar} {2}
* Artifact
* {1}, {T}: Put a charge counter on this artifact, then you may cast an instant or sorcery spell with mana value equal to the number of charge counters on this artifact from your hand without paying its mana cost.
* {3}, {T}, Remove X charge counters from this artifact: Scry X.
*/
private static final String brain = "Brain in a Jar";
@Test
public void test_scry2() {
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater");
addCard(Zone.LIBRARY, playerA, "Baleful Strix");
addCard(Zone.BATTLEFIELD, playerA, brain, 1);
addCard(Zone.BATTLEFIELD, playerA, "Dragon Appeasement"); // enchantement for skip draw
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}");
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}");
activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}");
setChoice(playerA, "X=2"); // how many counters to remove
addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2
setChoice(playerA, "Ancient Amphitheater"); // order for bottom
setStrictChooseMode(true);
setStopAt(7, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertCounterCount(playerA, brain, CounterType.CHARGE, 1);
}
}

View file

@ -1,6 +1,8 @@
package mage.abilities.effects.keyword; package mage.abilities.effects.keyword;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
@ -8,37 +10,45 @@ import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com, Susucr
*/ */
public class ScryEffect extends OneShotEffect { public class ScryEffect extends OneShotEffect {
protected final int scryNumber; protected DynamicValue amount;
protected final boolean showEffectHint; protected final boolean showEffectHint;
public ScryEffect(int scryNumber) { public ScryEffect(int scryNumber) {
this(scryNumber, true); this(scryNumber, true);
} }
public ScryEffect(DynamicValue amount) {
this(amount, false);
}
public ScryEffect(int scryNumber, boolean showEffectHint) { public ScryEffect(int scryNumber, boolean showEffectHint) {
this(StaticValue.get(scryNumber), showEffectHint);
}
public ScryEffect(DynamicValue amount, boolean showEffectHint) {
super(Outcome.Benefit); super(Outcome.Benefit);
this.scryNumber = scryNumber; this.amount = amount.copy();
this.showEffectHint = showEffectHint; this.showEffectHint = showEffectHint;
this.setText(); this.staticText = generateText();
} }
protected ScryEffect(final ScryEffect effect) { protected ScryEffect(final ScryEffect effect) {
super(effect); super(effect);
this.scryNumber = effect.scryNumber; this.amount = effect.amount.copy();
this.showEffectHint = effect.showEffectHint; this.showEffectHint = effect.showEffectHint;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
if (player != null) { if (player == null) {
return player.scry(scryNumber, source, game); return false;
} }
return false; return player.scry(this.amount.calculate(game, source, this), source, game);
} }
@Override @Override
@ -46,19 +56,24 @@ public class ScryEffect extends OneShotEffect {
return new ScryEffect(this); return new ScryEffect(this);
} }
private void setText() { private String generateText() {
StringBuilder sb = new StringBuilder("scry ").append(scryNumber); StringBuilder sb = new StringBuilder("scry ");
if (!showEffectHint) { String value = amount.toString();
staticText = sb.toString(); sb.append(CardUtil.numberToText(value));
return; String message = amount.getMessage();
if (!message.isEmpty()) {
sb.append(value.equals("X") ? ", where X is " : " for each ");
sb.append(message);
} }
if (scryNumber == 1) { if (showEffectHint) {
sb.append(". <i>(Look at the top card of your library. You may put that card on the bottom.)</i>"); if (value == "1") {
} else { sb.append(". <i>(Look at the top card of your library. You may put that card on the bottom.)</i>");
sb.append(". <i>(Look at the top "); } else {
sb.append(CardUtil.numberToText(scryNumber)); sb.append(". <i>(Look at the top ");
sb.append(" cards of your library, then put any number of them on the bottom and the rest on top in any order.)</i>"); sb.append(CardUtil.numberToText(value));
sb.append(" cards of your library, then put any number of them on the bottom and the rest on top in any order.)</i>");
}
} }
staticText = sb.toString(); return sb.toString();
} }
} }