Merge origin/master

This commit is contained in:
Styxo 2016-04-06 20:23:15 +02:00
commit 7ff31fb12e
92 changed files with 2894 additions and 775 deletions

View file

@ -183,19 +183,22 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
if (e.getButton() == MouseEvent.BUTTON1) {
Object obj = e.getSource();
if (obj instanceof MageCard) {
this.cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "pick-a-card");
this.hidePopup();
AudioManager.playOnDraftSelect();
}
}
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) { // only left click select
Object obj = e.getSource();
if (obj instanceof MageCard) {
this.cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "pick-a-card");
this.hidePopup();
AudioManager.playOnDraftSelect();
}
}
if (e.getButton() == MouseEvent.BUTTON3) { // only right click mark
if (e.getButton() == MouseEvent.BUTTON1 || e.getButton() == MouseEvent.BUTTON3) { // left or right click
Object obj = e.getSource();
if (obj instanceof MageCard) {
if (this.markedCard != null) {

View file

@ -154,6 +154,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_PASS_PRIORITY_CAST = "passPriorityCast";
public static final String KEY_PASS_PRIORITY_ACTIVATION = "passPriorityActivation";
public static final String KEY_AUTO_ORDER_TRIGGER = "autoOrderTrigger";
public static final String KEY_USE_FIRST_MANA_ABILITY = "useFirstManaAbility";
// mana auto payment
public static final String KEY_GAME_MANA_AUTOPAYMENT = "gameManaAutopayment";
@ -3273,7 +3274,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_CAST, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_ACTIVATION, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_AUTO_ORDER_TRIGGER, "true").equals("true")
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_AUTO_ORDER_TRIGGER, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_FIRST_MANA_ABILITY, "true").equals("true")
);
}

View file

@ -129,6 +129,7 @@ import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_ABILITY_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_FIRST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.UseFirstManaAbilityMode;
import mage.constants.Zone;
import mage.game.events.PlayerQueryEvent;
import mage.remote.Session;
@ -594,7 +595,9 @@ public final class GamePanel extends javax.swing.JPanel {
// default menu states
setMenuStates(
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true"),
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true"));
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true"),
false
);
updateGame(game);
}
@ -893,10 +896,11 @@ public final class GamePanel extends javax.swing.JPanel {
*
* @param manaPoolAutomatic
* @param manaPoolAutomaticRestricted
* @param useFirstManaAbility
*/
public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) {
public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted, boolean useFirstManaAbility) {
for (PlayAreaPanel playAreaPanel : players.values()) {
playAreaPanel.setMenuStates(manaPoolAutomatic, manaPoolAutomaticRestricted);
playAreaPanel.setMenuStates(manaPoolAutomatic, manaPoolAutomaticRestricted, useFirstManaAbility);
}
}
@ -1589,6 +1593,19 @@ public final class GamePanel extends javax.swing.JPanel {
}
});
KeyStroke ksAlt1 = KeyStroke.getKeyStroke(KeyEvent.VK_1, InputEvent.ALT_MASK);
this.getInputMap(c).put(ksAlt1, "USEFIRSTMANAABILITY");
this.getActionMap().put("USEFIRSTMANAABILITY", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
session.sendPlayerAction(PlayerAction.USE_FIRST_MANA_ABILITY_ON, gameId, null);
setMenuStates(
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true"),
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true"),
true);
}
});
final BasicSplitPaneUI myUi = (BasicSplitPaneUI) jSplitPane0.getUI();
final BasicSplitPaneDivider divider = myUi.getDivider();
final JButton upArrowButton = (JButton) divider.getComponent(0);
@ -1619,6 +1636,19 @@ public final class GamePanel extends javax.swing.JPanel {
}
});
KeyStroke ksAlt1Released = KeyStroke.getKeyStroke(KeyEvent.VK_1, InputEvent.ALT_MASK, true);
this.getInputMap(c).put(ksAlt1Released, "USEFIRSTMANAABILITY_RELEASE");
this.getActionMap().put("USEFIRSTMANAABILITY_RELEASE", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
session.sendPlayerAction(PlayerAction.USE_FIRST_MANA_ABILITY_OFF, gameId, null);
setMenuStates(
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true"),
PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true"),
false);
}
});
btnSwitchHands.setContentAreaFilled(false);
btnSwitchHands.setBorder(new EmptyBorder(0, 0, 0, 0));
btnSwitchHands.setIcon(new ImageIcon(ImageManagerImpl.getInstance().getSwitchHandsButtonImage()));

View file

@ -54,6 +54,7 @@ import mage.client.dialog.PreferencesDialog;
import static mage.client.dialog.PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS;
import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT;
import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE;
import static mage.client.dialog.PreferencesDialog.KEY_USE_FIRST_MANA_ABILITY;
import mage.client.util.GUISizeHelper;
import mage.constants.PlayerAction;
import mage.view.PlayerView;
@ -75,6 +76,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
private JCheckBoxMenuItem manaPoolMenuItem1;
private JCheckBoxMenuItem manaPoolMenuItem2;
private JCheckBoxMenuItem useFirstManaAbilityItem;
private JCheckBoxMenuItem allowViewHandCardsMenuItem;
public static final int PANEL_HEIGHT = 242;
@ -263,7 +265,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
public void actionPerformed(ActionEvent e) {
boolean manaPoolAutomatic = ((JCheckBoxMenuItem) e.getSource()).getState();
PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT, manaPoolAutomatic ? "true" : "false");
gamePanel.setMenuStates(manaPoolAutomatic, manaPoolMenuItem2.getState());
gamePanel.setMenuStates(manaPoolAutomatic, manaPoolMenuItem2.getState(), useFirstManaAbilityItem.getState());
gamePanel.getSession().sendPlayerAction(manaPoolAutomatic ? PlayerAction.MANA_AUTO_PAYMENT_ON : PlayerAction.MANA_AUTO_PAYMENT_OFF, gameId, null);
}
});
@ -281,9 +283,27 @@ public class PlayAreaPanel extends javax.swing.JPanel {
public void actionPerformed(ActionEvent e) {
boolean manaPoolAutomaticRestricted = ((JCheckBoxMenuItem) e.getSource()).getState();
PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, manaPoolAutomaticRestricted ? "true" : "false");
gamePanel.setMenuStates(manaPoolMenuItem1.getState(), manaPoolAutomaticRestricted);
gamePanel.setMenuStates(manaPoolMenuItem1.getState(), manaPoolAutomaticRestricted, useFirstManaAbilityItem.getState());
gamePanel.getSession().sendPlayerAction(manaPoolAutomaticRestricted ? PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_ON : PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_OFF, gameId, null);
}
});
useFirstManaAbilityItem = new JCheckBoxMenuItem("Use first mana ability when tapping lands", false);
useFirstManaAbilityItem.setMnemonic(KeyEvent.VK_F);
useFirstManaAbilityItem.setToolTipText("<html>Use the first mana ability when<br>"
+ " tapping lands for mana<br>"
+ "You can hold Alt+1 whilst tapping lands to use this feature");
manaPoolMenu.add(useFirstManaAbilityItem);
// Use first mana ability of lands
useFirstManaAbilityItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean useFirstManaAbility = ((JCheckBoxMenuItem) e.getSource()).getState();
PreferencesDialog.saveValue(KEY_USE_FIRST_MANA_ABILITY, useFirstManaAbility ? "true" : "false");
gamePanel.setMenuStates(manaPoolMenuItem1.getState(), manaPoolMenuItem2.getState(), useFirstManaAbility);
gamePanel.getSession().sendPlayerAction(useFirstManaAbility ? PlayerAction.USE_FIRST_MANA_ABILITY_ON: PlayerAction.USE_FIRST_MANA_ABILITY_OFF, gameId, null);
}
});
JMenu automaticConfirmsMenu = new JMenu("Automatic confirms");
@ -610,13 +630,16 @@ public class PlayAreaPanel extends javax.swing.JPanel {
this.playingMode = playingMode;
}
public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) {
public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted, boolean useFirstManaAbility) {
if (manaPoolMenuItem1 != null) {
manaPoolMenuItem1.setSelected(manaPoolAutomatic);
}
if (manaPoolMenuItem2 != null) {
manaPoolMenuItem2.setSelected(manaPoolAutomaticRestricted);
}
if (useFirstManaAbilityItem != null) {
useFirstManaAbilityItem.setSelected(useFirstManaAbility);
}
}
private mage.client.game.BattlefieldPanel battlefieldPanel;

View file

@ -397,7 +397,8 @@ public class CallbackClientImpl implements CallbackClient {
.append("<br/><b>F5</b> - Skip to next end step but stop on declare attackers/blockers and something on the stack")
.append("<br/><b>F7</b> - Skip to next main phase but stop on declare attackers/blockers and something on the stack")
.append("<br/><b>F9</b> - Skip everything until your next turn")
.append("<br/><b>F3</b> - Undo F4/F5/F7/F9").toString(),
.append("<br/><b>F11</b> - Skip everything until the end step just prior to your turn")
.append("<br/><b>F3</b> - Undo F4/F5/F7/F9/F11").toString(),
null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break;
case TOURNAMENT:

View file

@ -32,7 +32,7 @@ package mage.deck;
* @author LevelX2
*/
public class DuelCommander extends Commander {
public DuelCommander() {
super("Duel Commander");
banned.add("Ancestral Recall");
@ -44,6 +44,7 @@ public class DuelCommander extends Commander {
banned.add("Entomb");
banned.add("Fastbond");
banned.add("Food Chain");
banned.add("Gaea's Cradle");
banned.add("Gifts Ungiven");
banned.add("Grim Monolith");
banned.add("Grindstone");
@ -86,6 +87,8 @@ public class DuelCommander extends Commander {
bannedCommander.add("Erayo, Soratami Ascendant");
bannedCommander.add("Oloro, Ageless Ascetic");
bannedCommander.add("Rofellos, Llanowar Emissary");
bannedCommander.add("Tasigur, the Golden Fang");
bannedCommander.add("Yisan, the Wanderer Bard");
bannedCommander.add("Zur the Enchanter");
}

View file

@ -24,7 +24,7 @@
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
*/
package mage.deck;
import java.util.Date;
@ -38,20 +38,19 @@ import mage.constants.SetType;
*
* @author LevelX2
*/
public class Modern extends Constructed {
public Modern() {
super("Constructed - Modern");
Date cutoff = new GregorianCalendar(2003, 7, 28).getTime(); // Eight edition release date
for (ExpansionSet set: Sets.getInstance().values()) {
if ((set.getReleaseDate().after(cutoff) || set.getReleaseDate().equals(cutoff)) &&
(set.getSetType() == SetType.CORE || set.getSetType() == SetType.EXPANSION)) {
for (ExpansionSet set : Sets.getInstance().values()) {
if ((set.getReleaseDate().after(cutoff) || set.getReleaseDate().equals(cutoff))
&& (set.getSetType() == SetType.CORE || set.getSetType() == SetType.EXPANSION)) {
setCodes.add(set.getCode());
}
}
banned.add("Ancestral Vision");
banned.add("Ancient Den");
banned.add("Birthing Pod");
banned.add("Blazing Shoal");
@ -62,6 +61,7 @@ public class Modern extends Constructed {
banned.add("Deathrite Shaman");
banned.add("Dig Through Time");
banned.add("Dread Return");
banned.add("Eye of Ugin");
banned.add("Glimpse of Nature");
banned.add("Great Furnace");
banned.add("Green Sun's Zenith");
@ -80,7 +80,6 @@ public class Modern extends Constructed {
banned.add("Skullclamp");
banned.add("Splinter Twin");
banned.add("Summer Bloom");
banned.add("Sword of the Meek");
banned.add("Treasure Cruise");
banned.add("Tree of Tales");
banned.add("Umezawa's Jitte");

View file

@ -24,8 +24,7 @@
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
*/
package mage.deck;
import mage.cards.decks.Constructed;
@ -64,7 +63,7 @@ public class Vintage extends Constructed {
banned.add("Timmerian Fiends");
banned.add("Unexpected Potential");
banned.add("Worldknit");
restricted.add("Ancestral Recall");
restricted.add("Balance");
restricted.add("Black Lotus");
@ -79,6 +78,7 @@ public class Vintage extends Constructed {
restricted.add("Imperial Seal");
restricted.add("Library of Alexandria");
restricted.add("Lions Eye Diamond");
restricted.add("Lodestone Golem");
restricted.add("Lotus Petal");
restricted.add("Mana Crypt");
restricted.add("Mana Vault");
@ -108,6 +108,5 @@ public class Vintage extends Constructed {
restricted.add("Yawgmoths Bargain");
restricted.add("Yawgmoths Will");
}
}

View file

@ -59,6 +59,7 @@ import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.Constants;
import mage.constants.ManaType;
import mage.constants.Outcome;
@ -1249,7 +1250,16 @@ public class HumanPlayer extends PlayerImpl {
return;
}
}
if (userData.isUseFirstManaAbility() && object instanceof Permanent && object.getCardType().contains(CardType.LAND)){
ActivatedAbility ability = abilities.values().iterator().next();
if (ability instanceof ManaAbility) {
activateAbility(ability, game);
return;
}
}
game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(abilities.values()));
waitForResponse(game);
if (response.getUUID() != null && isInGame()) {
if (abilities.containsKey(response.getUUID())) {

View file

@ -556,6 +556,12 @@ public class GameController implements GameCallback {
case MANA_AUTO_PAYMENT_RESTRICTED_ON:
game.setManaPaymentModeRestricted(getPlayerId(userId), true);
break;
case USE_FIRST_MANA_ABILITY_ON:
game.setUseFirstManaAbility(getPlayerId(userId), true);
break;
case USE_FIRST_MANA_ABILITY_OFF:
game.setUseFirstManaAbility(getPlayerId(userId), false);
break;
case ADD_PERMISSION_TO_SEE_HAND_CARDS:
if (data instanceof UUID) {
UUID playerId = getPlayerId(userId);

View file

@ -27,8 +27,6 @@
*/
package mage.sets.alarareborn;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -41,15 +39,13 @@ import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SubLayer;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.watchers.Watcher;
import mage.watchers.common.FirstSpellCastThisTurnWatcher;
/**
*
@ -63,7 +59,6 @@ public class MaelstromNexus extends CardImpl {
// The first spell you cast each turn has cascade.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MaelstromNexusGainCascadeFirstSpellEffect()), new FirstSpellCastThisTurnWatcher());
}
public MaelstromNexus(final MaelstromNexus card) {
@ -112,55 +107,4 @@ class MaelstromNexusGainCascadeFirstSpellEffect extends ContinuousEffectImpl {
}
return false;
}
}
class FirstSpellCastThisTurnWatcher extends Watcher {
Map<UUID, UUID> playerFirstSpellCast = new HashMap<>();
Map<UUID, UUID> playerFirstCastSpell = new HashMap<>();
public FirstSpellCastThisTurnWatcher() {
super("FirstSpellCastThisTurn", WatcherScope.GAME);
}
public FirstSpellCastThisTurnWatcher(final FirstSpellCastThisTurnWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case SPELL_CAST:
case CAST_SPELL:
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null && !playerFirstSpellCast.containsKey(spell.getControllerId())) {
if (event.getType().equals(EventType.SPELL_CAST)) {
playerFirstSpellCast.put(spell.getControllerId(), spell.getId());
} else if (event.getType().equals(EventType.CAST_SPELL)) {
playerFirstCastSpell.put(spell.getControllerId(), spell.getId());
}
}
}
}
@Override
public FirstSpellCastThisTurnWatcher copy() {
return new FirstSpellCastThisTurnWatcher(this);
}
@Override
public void reset() {
super.reset();
playerFirstSpellCast.clear();
playerFirstCastSpell.clear();
}
public UUID getIdOfFirstCastSpell(UUID playerId) {
if (playerFirstSpellCast.get(playerId) == null) {
return playerFirstCastSpell.get(playerId);
} else {
return playerFirstSpellCast.get(playerId);
}
}
}
}

View file

@ -29,9 +29,8 @@ package mage.sets.bornofthegods;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalAsThoughEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect;
@ -61,7 +60,7 @@ public class PillarOfWar extends CardImpl {
// As long as Pillar of War is enchanted, it can attack as though it didn't have defender.
Effect effect = new ConditionalAsThoughEffect(
new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield),
new EnchantedCondition());
new EnchantedSourceCondition());
effect.setText("As long as {this} is enchanted, it can attack as though it didn't have defender");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));

View file

@ -32,7 +32,7 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.OnEventTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -72,7 +72,7 @@ public class KitsuneMystic extends CardImpl {
// At the beginning of the end step, if Kitsune Mystic is enchanted by two or more Auras, flip it.
this.addAbility(new ConditionalTriggeredAbility(
new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new AutumnTailKitsuneSage())),
new EnchantedCondition(2), "At the beginning of the end step, if {this} is enchanted by two or more Auras, flip it."));
new EnchantedSourceCondition(2), "At the beginning of the end step, if {this} is enchanted by two or more Auras, flip it."));
}
public KitsuneMystic(final KitsuneMystic card) {

View file

@ -28,11 +28,7 @@
package mage.sets.darkascension;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition;
@ -40,6 +36,8 @@ import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
/**
@ -60,11 +58,6 @@ public class AfflictedDeserter extends CardImpl {
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller.
Ability ability1 = new WerewolfRansackerAbility();
ability1.setRuleVisible(false); // rule will be shown only on the other face of the card but triggers only on this side
this.addAbility(ability1);
// At the beginning of each upkeep, if no spells were cast last turn, transform Afflicted Deserter.
this.addAbility(new TransformAbility());
TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false);
@ -80,5 +73,3 @@ public class AfflictedDeserter extends CardImpl {
return new AfflictedDeserter(this);
}
}

View file

@ -151,7 +151,7 @@ class CurseOfEchoesEffect extends OneShotEffect {
if (!playerId.equals(spell.getControllerId())) {
Player player = game.getPlayer(playerId);
if (player.chooseUse(Outcome.Copy, chooseMessage, source, game)) {
Spell copy = spell.copySpell(source.getControllerId());;
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
copy.chooseNewTargets(game, playerId);
}

View file

@ -27,36 +27,24 @@
*/
package mage.sets.darkascension;
import mage.constants.*;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.filter.common.FilterCreaturePermanent;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
import mage.game.permanent.token.WolfToken;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
@ -79,9 +67,6 @@ public class HuntmasterOfTheFells extends CardImpl {
// Whenever this creature enters the battlefield or transforms into Huntmaster of the Fells, put a 2/2 green Wolf creature token onto the battlefield and you gain 2 life.
this.addAbility(new HuntmasterOfTheFellsAbility());
// Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls.
this.addAbility(new RavagerOfTheFellsAbility());
// At the beginning of each upkeep, if no spells were cast last turn, transform Huntmaster of the Fells.
this.addAbility(new TransformAbility());
TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false);
@ -113,8 +98,6 @@ class HuntmasterOfTheFellsAbility extends TriggeredAbilityImpl {
public HuntmasterOfTheFellsAbility copy() {
return new HuntmasterOfTheFellsAbility(this);
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
@ -126,12 +109,12 @@ class HuntmasterOfTheFellsAbility extends TriggeredAbilityImpl {
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.TRANSFORMED && event.getTargetId().equals(this.getSourceId())) {
@ -150,141 +133,4 @@ class HuntmasterOfTheFellsAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever this creature enters the battlefield or transforms into {this}, put a 2/2 green Wolf creature token onto the battlefield and you gain 2 life.";
}
}
class RavagerOfTheFellsAbility extends TriggeredAbilityImpl {
public RavagerOfTheFellsAbility() {
super(Zone.BATTLEFIELD, new RavagerOfTheFellsEffect(), false);
Target target1 = new TargetOpponent();
this.addTarget(target1);
this.addTarget(new RavagerOfTheFellsTarget());
// Rule only shown on the night side
this.setRuleVisible(false);
}
public RavagerOfTheFellsAbility(final RavagerOfTheFellsAbility ability) {
super(ability);
}
@Override
public RavagerOfTheFellsAbility copy() {
return new RavagerOfTheFellsAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls.";
}
}
class RavagerOfTheFellsEffect extends OneShotEffect {
public RavagerOfTheFellsEffect() {
super(Outcome.Damage);
}
public RavagerOfTheFellsEffect(final RavagerOfTheFellsEffect effect) {
super(effect);
}
@Override
public RavagerOfTheFellsEffect copy() {
return new RavagerOfTheFellsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget());
if (player != null) {
player.damage(2, source.getSourceId(), game, false, true);
}
Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
if (creature != null) {
creature.damage(2, source.getSourceId(), game, false, true);
}
return true;
}
}
class RavagerOfTheFellsTarget extends TargetPermanent {
public RavagerOfTheFellsTarget() {
super(0, 1, new FilterCreaturePermanent(), false);
}
public RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) {
super(target);
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
UUID firstTarget = source.getFirstTarget();
Permanent permanent = game.getPermanent(id);
if (firstTarget != null && permanent != null && permanent.getControllerId().equals(firstTarget)) {
return super.canTarget(id, source, game);
}
return false;
}
@Override
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceId, sourceControllerId, game);
Set<UUID> possibleTargets = new HashSet<>();
MageObject object = game.getObject(sourceId);
for (StackObject item: game.getState().getStack()) {
if (item.getId().equals(sourceId)) {
object = item;
}
if (item.getSourceId().equals(sourceId)) {
object = item;
}
}
if (object instanceof StackObject) {
UUID playerId = ((StackObject)object).getStackAbility().getFirstTarget();
for (UUID targetId : availablePossibleTargets) {
Permanent permanent = game.getPermanent(targetId);
if(permanent != null && permanent.getControllerId().equals(playerId)){
possibleTargets.add(targetId);
}
}
}
return possibleTargets;
}
@Override
public RavagerOfTheFellsTarget copy() {
return new RavagerOfTheFellsTarget(this);
}
}

View file

@ -27,24 +27,36 @@
*/
package mage.sets.darkascension;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
/**
*
@ -52,8 +64,6 @@ import mage.constants.Zone;
*/
public class RavagerOfTheFells extends CardImpl {
private static final String rule = "Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls";
public RavagerOfTheFells(UUID ownerId) {
super(ownerId, 140, "Ravager of the Fells", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "");
this.expansionSetCode = "DKA";
@ -71,7 +81,7 @@ public class RavagerOfTheFells extends CardImpl {
this.addAbility(TrampleAbility.getInstance());
// Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(rule)));
this.addAbility(new RavagerOfTheFellsAbility());
// At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ravager of the Fells.
TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false);
@ -87,3 +97,136 @@ public class RavagerOfTheFells extends CardImpl {
return new RavagerOfTheFells(this);
}
}
class RavagerOfTheFellsAbility extends TriggeredAbilityImpl {
public RavagerOfTheFellsAbility() {
super(Zone.BATTLEFIELD, new RavagerOfTheFellsEffect(), false);
Target target1 = new TargetOpponent();
this.addTarget(target1);
this.addTarget(new RavagerOfTheFellsTarget());
}
public RavagerOfTheFellsAbility(final RavagerOfTheFellsAbility ability) {
super(ability);
}
@Override
public RavagerOfTheFellsAbility copy() {
return new RavagerOfTheFellsAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls.";
}
}
class RavagerOfTheFellsEffect extends OneShotEffect {
public RavagerOfTheFellsEffect() {
super(Outcome.Damage);
}
public RavagerOfTheFellsEffect(final RavagerOfTheFellsEffect effect) {
super(effect);
}
@Override
public RavagerOfTheFellsEffect copy() {
return new RavagerOfTheFellsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget());
if (player != null) {
player.damage(2, source.getSourceId(), game, false, true);
}
Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
if (creature != null) {
creature.damage(2, source.getSourceId(), game, false, true);
}
return true;
}
}
class RavagerOfTheFellsTarget extends TargetPermanent {
public RavagerOfTheFellsTarget() {
super(0, 1, new FilterCreaturePermanent(), false);
}
public RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) {
super(target);
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
UUID firstTarget = source.getFirstTarget();
Permanent permanent = game.getPermanent(id);
if (firstTarget != null && permanent != null && permanent.getControllerId().equals(firstTarget)) {
return super.canTarget(id, source, game);
}
return false;
}
@Override
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceId, sourceControllerId, game);
Set<UUID> possibleTargets = new HashSet<>();
MageObject object = game.getObject(sourceId);
for (StackObject item : game.getState().getStack()) {
if (item.getId().equals(sourceId)) {
object = item;
}
if (item.getSourceId().equals(sourceId)) {
object = item;
}
}
if (object instanceof StackObject) {
UUID playerId = ((StackObject) object).getStackAbility().getFirstTarget();
for (UUID targetId : availablePossibleTargets) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null && permanent.getControllerId().equals(playerId)) {
possibleTargets.add(targetId);
}
}
}
return possibleTargets;
}
@Override
public RavagerOfTheFellsTarget copy() {
return new RavagerOfTheFellsTarget(this);
}
}

View file

@ -33,11 +33,9 @@ import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
@ -75,7 +73,7 @@ public class WerewolfRansacker extends CardImpl {
this.toughness = new MageInt(4);
// Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(WerewolfRansackerAbility.RULE_TEXT)));
this.addAbility(new WerewolfRansackerAbility());
// At the beginning of each upkeep, if a player cast two or more spells last turn, transform Werewolf Ransacker.
TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false);
@ -93,9 +91,9 @@ public class WerewolfRansacker extends CardImpl {
}
class WerewolfRansackerAbility extends TriggeredAbilityImpl {
public static final String RULE_TEXT = "Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller";
public WerewolfRansackerAbility() {
super(Zone.BATTLEFIELD, new WerewolfRansackerEffect(), true);
Target target = new TargetPermanent(new FilterArtifactPermanent());
@ -159,8 +157,9 @@ class WerewolfRansackerEffect extends OneShotEffect {
affectedTargets++;
if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) {
Player player = game.getPlayer(permanent.getControllerId());
if (player != null)
if (player != null) {
player.damage(3, source.getSourceId(), game, false, true);
}
}
}
}

View file

@ -30,7 +30,7 @@ package mage.sets.darksteel;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -58,9 +58,9 @@ public class AuriokGlaivemaster extends CardImpl {
this.power = new MageInt(1);
this.toughness = new MageInt(1);
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedCondition.getInstance(), rule1);
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedSourceCondition.getInstance(), rule1);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect1));
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), EquippedCondition.getInstance(), rule2);
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), EquippedSourceCondition.getInstance(), rule2);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2));
}

View file

@ -32,7 +32,7 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
@ -60,7 +60,7 @@ public class FlaringFlameKin extends CardImpl {
this.toughness = new MageInt(2);
// As long as Flaring Flame-Kin is enchanted, it gets +2/+2, has trample, and has "{R}: Flaring Flame-Kin gets +1/+0 until end of turn."
EnchantedCondition enchanted = new EnchantedCondition();
EnchantedSourceCondition enchanted = new EnchantedSourceCondition();
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), enchanted,
"As long as {this} is enchanted, it gets +2/+2"));

View file

@ -35,7 +35,7 @@ import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.DamageTargetEffect;
@ -68,7 +68,7 @@ public class FreewindEquenaut extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(
new GainAbilitySourceEffect(ability, Duration.WhileOnBattlefield),
new EnchantedCondition(),
new EnchantedSourceCondition(),
"As long as {this} is enchanted, it has \"{T}: {this} deals 2 damage to target attacking or blocking creature\"")));
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
@ -74,12 +74,12 @@ public class RakshaGoldenCub extends CardImpl {
// As long as Raksha Golden Cub is equipped, Cat creatures you control get +2/+2 and have double strike.
Effect effect1 = new ConditionalContinuousEffect(
new BoostAllEffect(2, 2, Duration.WhileOnBattlefield, filter, false),
EquippedCondition.getInstance(),
EquippedSourceCondition.getInstance(),
"As long as {this} is equipped, Cat creatures you control get +2/+2");
Effect effect2 = new ConditionalContinuousEffect(
new GainAbilityAllEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, false),
EquippedCondition.getInstance(),
EquippedSourceCondition.getInstance(),
"As long as {this} is equipped, Cat creatures you control have double strike");
effect2.setText("and have double strike");

View file

@ -28,17 +28,17 @@
package mage.sets.guildpact;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
/**
*
@ -58,7 +58,7 @@ public class SkyriderTrainee extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(
new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield),
new EnchantedCondition(),
new EnchantedSourceCondition(),
"{this} has flying as long as it's enchanted")));
}

View file

@ -0,0 +1,100 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.homelands;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.counters.CounterType;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPlayer;
/**
*
* @author spjspj
*/
public class Leeches extends CardImpl {
public Leeches(UUID ownerId) {
super(ownerId, 111, "Leeches", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{W}{W}");
this.expansionSetCode = "HML";
// Target player loses all poison counters. Leeches deals that much damage to that player.
this.getSpellAbility().addTarget(new TargetPlayer());
this.getSpellAbility().addEffect(new LeechesEffect());
}
public Leeches(final Leeches card) {
super(card);
}
@Override
public Leeches copy() {
return new Leeches(this);
}
}
class LeechesEffect extends OneShotEffect {
public LeechesEffect() {
super(Outcome.Benefit);
this.staticText = "Target player loses all poison counters. Leeches deals that much damage to that player";
}
public LeechesEffect(final LeechesEffect effect) {
super(effect);
}
@Override
public LeechesEffect copy() {
return new LeechesEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(source.getFirstTarget());
if (targetPlayer == null) {
return false;
}
int countPoisonCounters = targetPlayer.getCounters().getCount(CounterType.POISON);
if (countPoisonCounters > 0) {
targetPlayer.getCounters().removeCounter(CounterType.POISON, countPoisonCounters);
targetPlayer.damage(countPoisonCounters, source.getSourceId(), game, false, true);
return true;
}
return false;
}
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -59,7 +59,7 @@ public class ThranGolem extends CardImpl {
this.toughness = new MageInt(3);
// As long as Thran Golem is enchanted, it gets +2/+2 and has flying, first strike, and trample.
EnchantedCondition enchanted = new EnchantedCondition();
EnchantedSourceCondition enchanted = new EnchantedSourceCondition();
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), enchanted,
"As long as {this} is enchanted, it gets +2/+2"));

View file

@ -0,0 +1,52 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.masterseditioniv;
import java.util.UUID;
/**
*
* @author spjspj
*/
public class Leeches extends mage.sets.homelands.Leeches {
public Leeches(UUID ownerId) {
super(ownerId);
this.cardNumber = 18;
this.expansionSetCode = "ME4";
}
public Leeches(final Leeches card) {
super(card);
}
@Override
public Leeches copy() {
return new Leeches(this);
}
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
@ -73,7 +73,7 @@ public class AuriokSteelshaper extends CardImpl {
// As long as Auriok Steelshaper is equipped, each creature you control that's a Soldier or a Knight gets +1/+1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, soldiersOrKnights, false),
EquippedCondition.getInstance(),
EquippedSourceCondition.getInstance(),
"As long as {this} is equipped, each creature you control that's a Soldier or a Knight gets +1/+1"
)));
}

View file

@ -33,7 +33,7 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -62,9 +62,9 @@ public class LeoninDenGuard extends CardImpl {
this.toughness = new MageInt(3);
// As long as Leonin Den-Guard is equipped, it gets +1/+1 and has vigilance.
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedCondition.getInstance(), rule1);
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedSourceCondition.getInstance(), rule1);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect1));
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance()), EquippedCondition.getInstance(), rule2);
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance()), EquippedSourceCondition.getInstance(), rule2);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2));
}

View file

@ -33,7 +33,7 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
@ -59,7 +59,7 @@ public class DreampodDruid extends CardImpl {
// At the beginning of each upkeep, if Dreampod Druid is enchanted, put a 1/1 green Saproling creature token onto the battlefield.
this.addAbility(new ConditionalTriggeredAbility(
new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken(),1), TargetController.ANY, false, false),
new EnchantedCondition(),
new EnchantedSourceCondition(),
"At the beginning of each upkeep, if Dreampod Druid is enchanted, put a 1/1 green Saproling creature token onto the battlefield."));
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.keyword.FlyingAbility;
@ -63,7 +63,7 @@ public class KrondTheDawnClad extends CardImpl {
// Whenever Krond the Dawn-Clad attacks, if it's enchanted, exile target permanent.
Ability ability = new ConditionalTriggeredAbility(
new AttacksTriggeredAbility(new ExileTargetEffect(), false),
new EnchantedCondition(),
new EnchantedSourceCondition(),
"Whenever {this} attacks, if it's enchanted, exile target permanent.");
ability.addTarget(new TargetPermanent());
this.addAbility(ability);

View file

@ -85,6 +85,7 @@ class ConcertedEffortEffect extends OneShotEffect {
private static final FilterControlledCreaturePermanent filterProtection = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterTrample = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterVigilance = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterCreatures = new FilterControlledCreaturePermanent();
static {
filterFlying.add(new AbilityPredicate(FlyingAbility.class));
@ -115,29 +116,29 @@ class ConcertedEffortEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Fear
if (game.getBattlefield().contains(filterFear, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FearAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(FearAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Landwalk
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterLandwalk, source.getControllerId(), game)) {
for (Ability ability : permanent.getAbilities(game)) {
if (ability instanceof LandwalkAbility) {
game.addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn, filterCreatures), source);
}
}
}
@ -146,19 +147,19 @@ class ConcertedEffortEffect extends OneShotEffect {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterProtection, source.getControllerId(), game)) {
for (Ability ability : permanent.getAbilities(game)) {
if (ability instanceof ProtectionAbility) {
game.addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn, filterCreatures), source);
}
}
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;
}

View file

@ -0,0 +1,200 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.ravnica;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardsImpl;
import mage.cards.SplitCard;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.filter.common.FilterInstantOrSorceryCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
*
* @author spjspj
*/
public class EyeOfTheStorm extends CardImpl {
public EyeOfTheStorm(UUID ownerId) {
super(ownerId, 48, "Eye of the Storm", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{5}{U}{U}");
this.expansionSetCode = "RAV";
// Whenever a player casts an instant or sorcery card, exile it. Then that player copies each instant or sorcery card exiled with Eye of the Storm. For each copy, the player may cast the copy without paying its mana cost.
this.addAbility(new EyeOfTheStormAbility());
}
public EyeOfTheStorm(final EyeOfTheStorm card) {
super(card);
}
@Override
public EyeOfTheStorm copy() {
return new EyeOfTheStorm(this);
}
}
class EyeOfTheStormAbility extends TriggeredAbilityImpl {
public EyeOfTheStormAbility() {
super(Zone.BATTLEFIELD, new EyeOfTheStormEffect1(), false);
}
public EyeOfTheStormAbility(final EyeOfTheStormAbility ability) {
super(ability);
}
@Override
public EyeOfTheStormAbility copy() {
return new EyeOfTheStormAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.SPELL_CAST;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getZone() == Zone.HAND) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
return true;
}
}
return false;
}
}
class EyeOfTheStormEffect1 extends OneShotEffect {
private static final FilterInstantOrSorceryCard instantOrSorceryfilter = new FilterInstantOrSorceryCard();
private static final FilterSpell filter = new FilterSpell("instant or sorcery card");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.INSTANT),
new CardTypePredicate(CardType.SORCERY)));
}
public EyeOfTheStormEffect1() {
super(Outcome.Neutral);
staticText = "Whenever a player casts an instant or sorcery card, exile it. Then that player copies each instant or sorcery card exiled with {this}. For each copy, the player may cast the copy without paying its mana cost";
}
public EyeOfTheStormEffect1(final EyeOfTheStormEffect1 effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
Permanent EyeOfTheStorm = game.getPermanentOrLKIBattlefield(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && spell != null && EyeOfTheStorm != null) {
Card card = spell.getCard();
if (card == null || !instantOrSorceryfilter.match(card, game)) {
return false;
}
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), EyeOfTheStorm.getZoneChangeCounter(game));
if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, EyeOfTheStorm.getIdName())) {
EyeOfTheStorm.imprint(card.getId(), game);
Player player = game.getPlayer(spell.getControllerId());
if (EyeOfTheStorm != null && EyeOfTheStorm.getImprinted() != null && EyeOfTheStorm.getImprinted().size() > 0 && controller != null) {
CardsImpl copiedCards = new CardsImpl();
for (UUID uuid : EyeOfTheStorm.getImprinted()) {
card = game.getCard(uuid);
if (card.isSplitCard()) {
copiedCards.add(((SplitCard) card).getLeftHalfCard());
copiedCards.add(((SplitCard) card).getRightHalfCard());
} else {
copiedCards.add(card);
}
}
boolean continueCasting = true;
while (continueCasting) {
continueCasting = copiedCards.size() > 1 && controller.chooseUse(outcome, "Cast one of the copied cards without paying its mana cost?", source, game);
Card cardToCopy;
if (copiedCards.size() == 1) {
cardToCopy = copiedCards.getCards(game).iterator().next();
} else {
TargetCard target = new TargetCard(1, Zone.EXILED, new FilterCard("card to copy"));
controller.choose(Outcome.Copy, copiedCards, target, game);
cardToCopy = copiedCards.get(target.getFirstTarget(), game);
copiedCards.remove(cardToCopy);
}
if (cardToCopy != null) {
Card copy = game.copyCard(cardToCopy, source, source.getControllerId());
if (controller.chooseUse(outcome, "Cast the copied card without paying mana cost?", source, game)) {
controller.cast(copy.getSpellAbility(), game, true);
}
}
}
return true;
}
}
}
return false;
}
@Override
public EyeOfTheStormEffect1 copy() {
return new EyeOfTheStormEffect1(this);
}
}

View file

@ -34,7 +34,7 @@ import mage.constants.Rarity;
import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.VigilanceAbility;
@ -58,7 +58,7 @@ public class GateHound extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(
new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent()),
new EnchantedCondition(),
new EnchantedSourceCondition(),
"Creatures you control have vigilance as long as {this} is enchanted")));
}

View file

@ -28,18 +28,17 @@
package mage.sets.scarsofmirrodin;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
/**
@ -59,10 +58,10 @@ public class SunspearShikari extends CardImpl {
// As long as Sunspear Shikari is equipped, it has first strike and lifelink.
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()),
EquippedCondition.getInstance(), "As long as {this} is equipped, it has first strike");
EquippedSourceCondition.getInstance(), "As long as {this} is equipped, it has first strike");
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1);
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance()),
EquippedCondition.getInstance(), "and lifelink");
EquippedSourceCondition.getInstance(), "and lifelink");
ability.addEffect(effect2);
this.addAbility(ability);
}

View file

@ -0,0 +1,161 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
/**
*
* @author halljared
*/
public class AccursedWitch extends CardImpl {
public AccursedWitch(UUID ownerId) {
super(ownerId, 97, "Accursed Witch", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.expansionSetCode = "SOI";
this.subtype.add("Human");
this.subtype.add("Shaman");
this.power = new MageInt(4);
this.toughness = new MageInt(2);
this.canTransform = true;
this.secondSideCard = new InfectiousCurse(ownerId);
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AccursedWitchSpellsCostReductionEffect()));
// When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
this.addAbility(new TransformAbility());
this.addAbility(new DiesTriggeredAbility(new AccursedWitchReturnTransformedEffect()));
}
public AccursedWitch(final AccursedWitch card) {
super(card);
}
@Override
public AccursedWitch copy() {
return new AccursedWitch(this);
}
}
class AccursedWitchReturnTransformedEffect extends OneShotEffect {
public AccursedWitchReturnTransformedEffect() {
super(Outcome.PutCardInPlay);
this.staticText = "Put {this} from your graveyard onto the battlefield transformed";
}
public AccursedWitchReturnTransformedEffect(final AccursedWitchReturnTransformedEffect effect) {
super(effect);
}
@Override
public AccursedWitchReturnTransformedEffect copy() {
return new AccursedWitchReturnTransformedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
if (game.getState().getZone(source.getSourceId()).equals(Zone.GRAVEYARD)) {
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
//note: should check for null after game.getCard
Card card = game.getCard(source.getSourceId());
if (card != null) {
card.putOntoBattlefield(game, Zone.BATTLEFIELD, source.getSourceId(), source.getControllerId(), false);
}
}
return true;
}
return false;
}
}
class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl {
public AccursedWitchSpellsCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.REDUCE_COST);
this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast.";
}
protected AccursedWitchSpellsCostReductionEffect(AccursedWitchSpellsCostReductionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
CardUtil.reduceCost(abilityToModify, 1);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if(game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (Target target : abilityToModify.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
Permanent permanent = game.getPermanent(targetUUID);
if(permanent != null && permanent.getId().equals(source.getSourceId())) {
return true;
}
}
}
}
}
return false;
}
@Override
public AccursedWitchSpellsCostReductionEffect copy() {
return new AccursedWitchSpellsCostReductionEffect(this);
}
}

View file

@ -27,12 +27,9 @@
*/
package mage.sets.shadowsoverinnistrad;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility;
@ -50,17 +47,12 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
@ -105,8 +97,6 @@ public class ArchangelAvacyn extends CardImpl {
this.addAbility(new TransformAbility());
this.addAbility(new DiesCreatureTriggeredAbility(new ArchangelAvacynEffect(), false, filter));
// When this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent.
this.addAbility(new AvacynThePurifierAbility());
}
public ArchangelAvacyn(final ArchangelAvacyn card) {
@ -145,85 +135,3 @@ class ArchangelAvacynEffect extends OneShotEffect {
return new ArchangelAvacynEffect(this);
}
}
class AvacynThePurifierAbility extends TriggeredAbilityImpl {
public AvacynThePurifierAbility() {
super(Zone.BATTLEFIELD, new AvacynThePurifierEffect(), false);
// Rule only shown on the night side
this.setRuleVisible(false);
}
public AvacynThePurifierAbility(final AvacynThePurifierAbility ability) {
super(ability);
}
@Override
public AvacynThePurifierAbility copy() {
return new AvacynThePurifierAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent.";
}
}
class AvacynThePurifierEffect extends OneShotEffect {
public AvacynThePurifierEffect() {
super(Outcome.Damage);
}
public AvacynThePurifierEffect(final AvacynThePurifierEffect effect) {
super(effect);
}
@Override
public AvacynThePurifierEffect copy() {
return new AvacynThePurifierEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature");
filter.add(new AnotherPredicate());
List<Permanent> permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game);
for (Permanent permanent: permanents) {
permanent.damage(3, source.getSourceId(), game, false, true);
}
for(UUID opponentId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(opponentId);
if (opponent != null) {
opponent.damage(3, source.getSourceId(), game, false, true);
}
}
return true;
}
}

View file

@ -27,15 +27,25 @@
*/
package mage.sets.shadowsoverinnistrad;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.InfoEffect;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
@ -43,8 +53,6 @@ import mage.constants.Zone;
*/
public class AvacynThePurifier extends CardImpl {
private static final String rule = "Whenever this creature transforms into {this}, it deals 3 damage to each other creature and each opponent";
public AvacynThePurifier(UUID ownerId) {
super(ownerId, 5, "Avacyn, the Purifier", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "");
this.expansionSetCode = "SOI";
@ -61,7 +69,7 @@ public class AvacynThePurifier extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// When this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(rule)));
this.addAbility(new AvacynThePurifierAbility());
}
public AvacynThePurifier(final AvacynThePurifier card) {
@ -73,3 +81,82 @@ public class AvacynThePurifier extends CardImpl {
return new AvacynThePurifier(this);
}
}
class AvacynThePurifierAbility extends TriggeredAbilityImpl {
public AvacynThePurifierAbility() {
super(Zone.BATTLEFIELD, new AvacynThePurifierEffect(), false);
}
public AvacynThePurifierAbility(final AvacynThePurifierAbility ability) {
super(ability);
}
@Override
public AvacynThePurifierAbility copy() {
return new AvacynThePurifierAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent.";
}
}
class AvacynThePurifierEffect extends OneShotEffect {
public AvacynThePurifierEffect() {
super(Outcome.Damage);
}
public AvacynThePurifierEffect(final AvacynThePurifierEffect effect) {
super(effect);
}
@Override
public AvacynThePurifierEffect copy() {
return new AvacynThePurifierEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature");
filter.add(new AnotherPredicate());
List<Permanent> permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game);
for (Permanent permanent : permanents) {
permanent.damage(3, source.getSourceId(), game, false, true);
}
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(opponentId);
if (opponent != null) {
opponent.damage(3, source.getSourceId(), game, false, true);
}
}
return true;
}
}

View file

@ -29,28 +29,15 @@ package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
/**
*
@ -71,13 +58,8 @@ public class AvacynianMissionaries extends CardImpl {
// At the beginning of your end step, if Avacynian Missionaries is equipped, transform it.
this.addAbility(new TransformAbility());
this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), TargetController.YOU, new EquippedCondition(), false));
this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), TargetController.YOU, new EquippedSourceCondition(), false));
// When this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield.
Ability ability = new LunarchInquisitorsAbility();
ability.addTarget(new TargetCreaturePermanent());
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility()));
this.addAbility(ability);
}
public AvacynianMissionaries(final AvacynianMissionaries card) {
@ -89,80 +71,3 @@ public class AvacynianMissionaries extends CardImpl {
return new AvacynianMissionaries(this);
}
}
class LunarchInquisitorsAbility extends TriggeredAbilityImpl {
public LunarchInquisitorsAbility() {
super(Zone.BATTLEFIELD, new LunarchInquisitorsExileEffect(), true);
// Rule only shown on the night side
this.setRuleVisible(false);
}
public LunarchInquisitorsAbility(final LunarchInquisitorsAbility ability) {
super(ability);
}
@Override
public LunarchInquisitorsAbility copy() {
return new LunarchInquisitorsAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield.";
}
}
class LunarchInquisitorsExileEffect extends OneShotEffect {
public LunarchInquisitorsExileEffect() {
super(Outcome.Benefit);
this.staticText = "exile target creature until {this} leaves the battlefield";
}
public LunarchInquisitorsExileEffect(final LunarchInquisitorsExileEffect effect) {
super(effect);
}
@Override
public LunarchInquisitorsExileEffect copy() {
return new LunarchInquisitorsExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
// If Lunarch Inquisitors leaves the battlefield before its triggered ability resolves,
// the target won't be exiled.
if (permanent != null) {
return new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()).apply(game, source);
}
return false;
}
}

View file

@ -29,12 +29,18 @@ package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
@ -42,8 +48,6 @@ import mage.constants.Zone;
*/
public class AwokenHorror extends CardImpl {
private static final String rule = "Whenever this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands";
public AwokenHorror(UUID ownerId) {
super(ownerId, 92, "Awoken Horror", Rarity.RARE, new CardType[]{CardType.CREATURE}, "");
this.expansionSetCode = "SOI";
@ -56,7 +60,7 @@ public class AwokenHorror extends CardImpl {
this.nightCard = true;
// When this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(rule)));
this.addAbility(new AwokenHorrorAbility());
}
public AwokenHorror(final AwokenHorror card) {
@ -68,3 +72,55 @@ public class AwokenHorror extends CardImpl {
return new AwokenHorror(this);
}
}
class AwokenHorrorAbility extends TriggeredAbilityImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Horror creatures");
static {
filter.add(Predicates.not(new SubtypePredicate("Horror")));
}
public AwokenHorrorAbility() {
super(Zone.BATTLEFIELD, new ReturnToHandFromBattlefieldAllEffect(filter), false);
}
public AwokenHorrorAbility(final AwokenHorrorAbility ability) {
super(ability);
}
@Override
public AwokenHorrorAbility copy() {
return new AwokenHorrorAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
// @Override
// public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
// Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
// if (currentSourceObject != null && currentSourceObject.isTransformed()) {
// // hard to check if the not transformed source hat the ability. But if it was transformed it probably had it, but maybe no perfect solution
// return true;
// }
// return false;
// }
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands.";
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowsoverinnistrad;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
/**
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class CreepingDread extends CardImpl {
public CreepingDread(UUID ownerId) {
super(ownerId, 104, "Creeping Dread", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}");
this.expansionSetCode = "SOI";
// At the beginning of your upkeep, each player discards a card. Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreepingDreadEffect(), TargetController.YOU, false));
}
public CreepingDread(final CreepingDread card) {
super(card);
}
@Override
public CreepingDread copy() {
return new CreepingDread(this);
}
}
class CreepingDreadEffect extends OneShotEffect {
public CreepingDreadEffect() {
super(Outcome.Detriment);
this.staticText = "each player discards a card. Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.";
}
public CreepingDreadEffect(final CreepingDreadEffect effect) {
super(effect);
}
@Override
public CreepingDreadEffect copy() {
return new CreepingDreadEffect(this);
}
/*
* When a spell or ability instructs each player to discard a card,
starting with the player whose turn it is and proceeding in turn order,
each player selects a card from his or her hand without revealing it,
sets it aside, and then all of those cards are revealed and discarded at once.
http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=409851
*/
@Override
public boolean apply(Game game, Ability source) {
// controller discards a card - store info on card type
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Set<CardType> typesChosen = new HashSet<>();
Map<Player,Card> cardsChosen = new HashMap<>();
if(!controller.getHand().isEmpty()) {
TargetCard controllerTarget = new TargetCard(Zone.HAND, new FilterCard());
if(controller.choose(Outcome.Discard, controller.getHand(), controllerTarget, game)) {
Card card = controller.getHand().get(controllerTarget.getFirstTarget(), game);
if (card != null) {
typesChosen = new HashSet<>(card.getCardType());
cardsChosen.put(controller, card);
}
}
}
Set<Player> opponentsAffected = new HashSet<>();
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(playerId);
// opponent discards a card - if it is same card type as controller, add to opponentsAffected
if(!opponent.getHand().isEmpty()) {
TargetCard target = new TargetCard(Zone.HAND, new FilterCard());
if(opponent.choose(Outcome.Discard, opponent.getHand(), target, game)) {
Card card = opponent.getHand().get(target.getFirstTarget(), game);
if (card != null) {
if (!typesChosen.isEmpty()) {
for (CardType cType : typesChosen) {
for (CardType oType : card.getCardType()) {
if (cType == oType) {
opponentsAffected.add(opponent);
break;
}
}
}
}
cardsChosen.put(opponent, card);
}
}
}
}
// everyone discards the card at the same time
if (!cardsChosen.isEmpty()) {
for (Map.Entry<Player, Card> entry : cardsChosen.entrySet()) {
Player player = entry.getKey();
Card cardChosen = entry.getValue();
if (player != null && cardChosen != null) {
player.discard(cardChosen, source, game);
}
}
}
// each opponent who discarded a card of the same type loses 3 life
if (!opponentsAffected.isEmpty()) {
for(Player opponent : opponentsAffected) {
opponent.loseLife(3, game);
}
}
return true;
}
return false;
}
}

View file

@ -74,6 +74,7 @@ public class DeclarationInStone extends CardImpl {
class DeclarationInStoneEffect extends OneShotEffect {
private static final FilterCreaturePermanent creaturesOnly = new FilterCreaturePermanent();
private static final FilterCreaturePermanent nonTokenFilter = new FilterCreaturePermanent("nontoken creature");
static{
nonTokenFilter.add(Predicates.not(new TokenPredicate()));
@ -106,7 +107,11 @@ class DeclarationInStoneEffect extends OneShotEffect {
String name = targetPermanent.getName();
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controllerPermanentId)) {
if (permanent != null && permanent.getName().equals(name)) {
you.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
// only exile creatures (reported bug on awakened lands targetted exiling all other lands of same name)
if (creaturesOnly.match(permanent, game)) {
you.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
}
// exiled count only matters for non-tokens
if (nonTokenFilter.match(permanent, game)) {

View file

@ -27,12 +27,13 @@
*/
package mage.sets.shadowsoverinnistrad;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
@ -100,7 +101,7 @@ class EngulfTheShoreEffect extends OneShotEffect {
int islands = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
FilterPermanent creatureFilter = new FilterCreaturePermanent();
creatureFilter.add(new ToughnessPredicate(Filter.ComparisonType.LessThan, islands + 1));
Cards cardsToHand = new CardsImpl();
Set<Card> cardsToHand = new HashSet<>();
for (Permanent permanent : game.getBattlefield().getActivePermanents(creatureFilter, source.getControllerId(), source.getSourceId(), game)) {
cardsToHand.add(permanent);
}

View file

@ -77,6 +77,7 @@ class ErdwalIlluminatorTriggeredAbility extends TriggeredAbilityImpl {
public ErdwalIlluminatorTriggeredAbility() {
super(Zone.BATTLEFIELD, new InvestigateEffect(), false);
addWatcher(new InvestigatedWatcher());
}
public ErdwalIlluminatorTriggeredAbility(final ErdwalIlluminatorTriggeredAbility ability) {
@ -126,9 +127,9 @@ class InvestigatedWatcher extends Watcher {
public void watch(GameEvent event, Game game) {
if (EventType.INVESTIGATED.equals(event.getType())) {
if (!timesInvestigated.containsKey(event.getPlayerId())) {
timesInvestigated.put(event.getPlayerId(), timesInvestigated.get(event.getPlayerId()) + 1);
} else {
timesInvestigated.put(event.getPlayerId(), 1);
} else {
timesInvestigated.put(event.getPlayerId(), timesInvestigated.get(event.getPlayerId()) + 1);
}
}
}

View file

@ -38,7 +38,6 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamageEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
@ -90,7 +89,9 @@ class FlamebladeAngelTriggeredAbility extends TriggeredAbilityImpl {
@java.lang.Override
public boolean checkEventType(GameEvent event, Game game) {
return event instanceof DamageEvent;
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE
|| event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@java.lang.Override

View file

@ -0,0 +1,104 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.keyword.MadnessAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.game.Game;
import mage.game.permanent.token.ZombieToken;
/**
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class FromUnderTheFloorboards extends CardImpl {
public FromUnderTheFloorboards(UUID ownerId) {
super(ownerId, 111, "From Under the Floorboards", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
this.expansionSetCode = "SOI";
// Madness {X}{B}{B} <i>(If you discard this card discard it into exile. When you do cast it for its madness cost or put it into your graveyard.
Ability ability = (new MadnessAbility(this, new ManaCostsImpl("{X}{B}{B}")));
ability.setRuleAtTheTop(true);
this.addAbility(ability);
// Put three 2/2 black Zombie creature tokens onto the battlefield tapped and you gain 3 life.
// If From Under the Floorboards's madness cost was paid, instead put X of those tokens onto the battlefield tapped and you gain X life.
DynamicValue xValue = new FromUnderTheFloorboardsManacostVariableValue();
Effect effect = new CreateTokenEffect(new ZombieToken(), xValue, true, false);
effect.setText("Put three 2/2 black Zombie creature tokens onto the battlefield tapped and you gain 3 life. If {this} madness cost was paid, instead put X of those tokens onto the battlefield tapped and you gain X life.");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new GainLifeEffect(xValue));
}
public FromUnderTheFloorboards(final FromUnderTheFloorboards card) {
super(card);
}
@Override
public FromUnderTheFloorboards copy() {
return new FromUnderTheFloorboards(this);
}
}
class FromUnderTheFloorboardsManacostVariableValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
ManaCosts manaCosts = sourceAbility.getManaCostsToPay();
if (manaCosts.getVariableCosts().isEmpty()) {
return 3;
}
return sourceAbility.getManaCostsToPay().getX();
}
@Override
public FromUnderTheFloorboardsManacostVariableValue copy() {
return new FromUnderTheFloorboardsManacostVariableValue();
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "";
}
}

View file

@ -51,10 +51,10 @@ import mage.game.permanent.Permanent;
*
* @author fireshoes
*/
public class GoldknightCastigator extends CardImpl {
public class GoldnightCastigator extends CardImpl {
public GoldknightCastigator(UUID ownerId) {
super(ownerId, 162, "Goldknight Castigator", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
public GoldnightCastigator(UUID ownerId) {
super(ownerId, 162, "Goldnight Castigator", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
this.expansionSetCode = "SOI";
this.subtype.add("Angel");
this.power = new MageInt(4);
@ -67,35 +67,35 @@ public class GoldknightCastigator extends CardImpl {
this.addAbility(HasteAbility.getInstance());
// If a source would deal damage to you, it deals double that damage to you instead.
// If a source would deal damage to Goldknight Castigator, it deals double that damage to Goldknight Castigator instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GoldknightCastigatorDoubleDamageEffect()));
// If a source would deal damage to Goldnight Castigator, it deals double that damage to Goldkight Castigator instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GoldnightCastigatorDoubleDamageEffect()));
}
public GoldknightCastigator(final GoldknightCastigator card) {
public GoldnightCastigator(final GoldnightCastigator card) {
super(card);
}
@Override
public GoldknightCastigator copy() {
return new GoldknightCastigator(this);
public GoldnightCastigator copy() {
return new GoldnightCastigator(this);
}
}
class GoldknightCastigatorDoubleDamageEffect extends ReplacementEffectImpl {
class GoldnightCastigatorDoubleDamageEffect extends ReplacementEffectImpl {
public GoldknightCastigatorDoubleDamageEffect() {
public GoldnightCastigatorDoubleDamageEffect() {
super(Duration.WhileOnBattlefield, Outcome.Damage);
staticText = "If a source would deal damage to you, it deals double that damage to you instead."
+ "<BR>If a source would deal damage to Goldknight Castigator, it deals double that damage to {this} instead.";
+ "<BR>If a source would deal damage to Goldnight Castigator, it deals double that damage to {this} instead.";
}
public GoldknightCastigatorDoubleDamageEffect(final GoldknightCastigatorDoubleDamageEffect effect) {
public GoldnightCastigatorDoubleDamageEffect(final GoldnightCastigatorDoubleDamageEffect effect) {
super(effect);
}
@Override
public GoldknightCastigatorDoubleDamageEffect copy() {
return new GoldknightCastigatorDoubleDamageEffect(this);
public GoldnightCastigatorDoubleDamageEffect copy() {
return new GoldnightCastigatorDoubleDamageEffect(this);
}
@Override

View file

@ -29,13 +29,15 @@ package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SacrificeAllTriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
/**
*
@ -52,7 +54,7 @@ public class GrafMole extends CardImpl {
this.toughness = new MageInt(4);
// Whenever you sacrifice a Clue, you gain 3 life.
this.addAbility(new SacrificeAllTriggeredAbility(new GainLifeEffect(3), new FilterCreaturePermanent("Clue", "a Clue"), TargetController.YOU, false));
this.addAbility(new GrafMoleTriggeredAbility());
}
public GrafMole(final GrafMole card) {
@ -64,3 +66,36 @@ public class GrafMole extends CardImpl {
return new GrafMole(this);
}
}
class GrafMoleTriggeredAbility extends TriggeredAbilityImpl {
public GrafMoleTriggeredAbility() {
super(Zone.BATTLEFIELD, new GainLifeEffect(3));
setLeavesTheBattlefieldTrigger(true);
}
public GrafMoleTriggeredAbility(final GrafMoleTriggeredAbility ability) {
super(ability);
}
@Override
public GrafMoleTriggeredAbility copy() {
return new GrafMoleTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.SACRIFICED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getPlayerId().equals(this.getControllerId())
&& game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD).getSubtype().contains("Clue");
}
@Override
public String getRule() {
return "Whenever you sacrifice a Clue, " + super.getRule();
}
}

View file

@ -103,8 +103,8 @@ class HarnessTheStormTriggeredAbility extends SpellCastControllerTriggeredAbilit
}
@Override
public SpellCastControllerTriggeredAbility copy() {
return super.copy(); //To change body of generated methods, choose Tools | Templates.
public HarnessTheStormTriggeredAbility copy() {
return new HarnessTheStormTriggeredAbility(this);
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
*
* @author halljared
*/
public class InfectiousCurse extends CardImpl {
public InfectiousCurse(UUID ownerId) {
super(ownerId, 97, "Infectious Curse", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "");
this.expansionSetCode = "SOI";
this.subtype.add("Aura");
this.subtype.add("Curse");
this.nightCard = true;
this.canTransform = true;
// Enchant player
TargetPlayer auraTarget = new TargetPlayer();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Damage));
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
// Spells you cast that target enchanted player cost {1} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfectiousCurseCostReductionEffect()));
// At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life.
InfectiousCurseAbility curseAbility = new InfectiousCurseAbility();
curseAbility.addEffect(new GainLifeEffect(1));
this.addAbility(curseAbility);
}
public InfectiousCurse(final InfectiousCurse card) {
super(card);
}
@Override
public InfectiousCurse copy() {
return new InfectiousCurse(this);
}
}
class InfectiousCurseAbility extends TriggeredAbilityImpl {
public InfectiousCurseAbility() {
super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1));
}
public InfectiousCurseAbility(final InfectiousCurseAbility ability) {
super(ability);
}
@Override
public InfectiousCurseAbility copy() {
return new InfectiousCurseAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent enchantment = game.getPermanent(this.sourceId);
if (enchantment != null && enchantment.getAttachedTo() != null) {
Player player = game.getPlayer(enchantment.getAttachedTo());
if (player != null && game.getActivePlayerId().equals(player.getId())) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId()));
return true;
}
}
return false;
}
@Override
public String getRule() {
return "At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life.";
}
}
class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl {
public InfectiousCurseCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
this.staticText = "Spells you cast that target enchanted player cost {1} less to cast.";
}
protected InfectiousCurseCostReductionEffect(InfectiousCurseCostReductionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
CardUtil.reduceCost(abilityToModify, 1);
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (source.getControllerId().equals(abilityToModify.getControllerId())) {
for (Target target : abilityToModify.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
Permanent enchantment = game.getPermanent(source.getSourceId());
UUID attachedTo = enchantment.getAttachedTo();
if (targetUUID.equals(attachedTo)) {
return true;
}
}
}
}
}
return false;
}
@Override
public InfectiousCurseCostReductionEffect copy() {
return new InfectiousCurseCostReductionEffect(this);
}
}

View file

@ -99,23 +99,23 @@ class JaceUnravelerOfSecretsEmblem extends Emblem {
this.setName("EMBLEM: Jace, Unraveler of Secrets");
Effect effect = new CounterTargetEffect();
effect.setText("counter that spell");
this.getAbilities().add(new JaceUnravelerOfSecretsTriggertAbility(effect, false));
this.getAbilities().add(new JaceUnravelerOfSecretsTriggeredAbility(effect, false));
}
}
class JaceUnravelerOfSecretsTriggertAbility extends SpellCastOpponentTriggeredAbility {
class JaceUnravelerOfSecretsTriggeredAbility extends SpellCastOpponentTriggeredAbility {
public JaceUnravelerOfSecretsTriggertAbility(Effect effect, boolean optional) {
public JaceUnravelerOfSecretsTriggeredAbility(Effect effect, boolean optional) {
super(effect, optional);
}
public JaceUnravelerOfSecretsTriggertAbility(SpellCastOpponentTriggeredAbility ability) {
public JaceUnravelerOfSecretsTriggeredAbility(SpellCastOpponentTriggeredAbility ability) {
super(ability);
}
@Override
public SpellCastOpponentTriggeredAbility copy() {
return super.copy(); //To change body of generated methods, choose Tools | Templates.
return super.copy();
}
@Override
@ -137,5 +137,4 @@ class JaceUnravelerOfSecretsTriggertAbility extends SpellCastOpponentTriggeredAb
public String getRule() {
return "Whenever an opponent casts his or her first spell each turn, counter that spell.";
}
}
}

View file

@ -29,12 +29,23 @@ package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.InfoEffect;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
/**
*
@ -57,7 +68,10 @@ public class LunarchInquisitors extends CardImpl {
this.nightCard = true;
// When this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(rule)));
Ability ability = new LunarchInquisitorsAbility();
ability.addTarget(new TargetCreaturePermanent());
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility()));
this.addAbility(ability);
}
public LunarchInquisitors(final LunarchInquisitors card) {
@ -69,3 +83,79 @@ public class LunarchInquisitors extends CardImpl {
return new LunarchInquisitors(this);
}
}
class LunarchInquisitorsAbility extends TriggeredAbilityImpl {
public LunarchInquisitorsAbility() {
super(Zone.BATTLEFIELD, new LunarchInquisitorsExileEffect(), true);
// Rule only shown on the night side
this.setRuleVisible(false);
}
public LunarchInquisitorsAbility(final LunarchInquisitorsAbility ability) {
super(ability);
}
@Override
public LunarchInquisitorsAbility copy() {
return new LunarchInquisitorsAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield.";
}
}
class LunarchInquisitorsExileEffect extends OneShotEffect {
public LunarchInquisitorsExileEffect() {
super(Outcome.Benefit);
this.staticText = "exile target creature until {this} leaves the battlefield";
}
public LunarchInquisitorsExileEffect(final LunarchInquisitorsExileEffect effect) {
super(effect);
}
@Override
public LunarchInquisitorsExileEffect copy() {
return new LunarchInquisitorsExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
// If Lunarch Inquisitors leaves the battlefield before its triggered ability resolves,
// the target won't be exiled.
if (permanent != null) {
return new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()).apply(game, source);
}
return false;
}
}

View file

@ -100,6 +100,7 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
private static final FilterControlledCreaturePermanent filterSkulk = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterTrample = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterVigilance = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterCreatures = new FilterControlledCreaturePermanent();
static {
filterFirstStrike.add(new AbilityPredicate(FirstStrikeAbility.class));
@ -136,67 +137,67 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Skulk
if (game.getBattlefield().contains(filterSkulk, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn), source);
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.condition.common.DeliriumCondition;
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
/**
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class PickTheBrain extends CardImpl {
public PickTheBrain(UUID ownerId) {
super(ownerId, 129, "Pick the Brain", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{B}");
this.expansionSetCode = "SOI";
// Target opponent reveals his or her hand. You choose a nonland card from it and exile that card.
// <i>Delirium</i> &mdash; If there are four or more card types among cards in your graveyard, search that player's graveyard, hand, and library for any number of cards with the same name as the exiled card, exile those cards, then that player shuffles his or her library.
this.getSpellAbility().addEffect(new PickTheBrainEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
public PickTheBrain(final PickTheBrain card) {
super(card);
}
@Override
public PickTheBrain copy() {
return new PickTheBrain(this);
}
}
class PickTheBrainEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
private static final FilterCard filter = new FilterCard("a nonland card");
static {
filter.add(Predicates.not(new CardTypePredicate(CardType.LAND)));
}
public PickTheBrainEffect() {
super(true, "that card's controller", "all cards with the same name as that card");
}
public PickTheBrainEffect(final PickTheBrainEffect effect) {
super(effect);
}
@Override
public PickTheBrainEffect copy() {
return new PickTheBrainEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player opponent = game.getPlayer(this.getTargetPointer().getFirst(game, source));
Player controller = game.getPlayer(source.getControllerId());
if (opponent != null && controller != null) {
if (!opponent.getHand().isEmpty()) {
opponent.revealCards("Exile " + filter.getMessage(), opponent.getHand(), game);
TargetCard target = new TargetCard(Zone.HAND, filter);
if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) {
Card card = opponent.getHand().get(target.getFirstTarget(), game);
if (card != null) {
controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.HAND, true);
// Check the Delirium condition
if (!DeliriumCondition.getInstance().apply(game, source)) {
return true;
}
return this.applySearchAndExile(game, source, card.getName(), opponent.getId());
}
}
}
}
return false;
}
@Override
public String getText(Mode mode) {
return "Target opponent reveals his or her hand. You choose a nonland card from it and exile that card.<br><br>"
+ "<i>Delirium</i> &mdash; If there are four or more card types among cards in your graveyard, "
+ "search that player's graveyard, hand, and library for any number of cards "
+ "with the same name as the exiled card, exile those cards, then that player shuffles his or her library";
}
}

View file

@ -41,20 +41,22 @@ import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.filter.predicate.other.OwnerPredicate;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.watchers.common.CastFromGraveyardWatcher;
/**
*
* @author LevelX2
*/
public class PrizedAmalgam extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature");
static {
filter.add(new ControllerPredicate(TargetController.YOU));
filter.add(new OwnerPredicate(TargetController.YOU));
}
public PrizedAmalgam(UUID ownerId) {
@ -66,7 +68,8 @@ public class PrizedAmalgam extends CardImpl {
// Whenever a creature enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, return Prized Amalgam from your graveyard to the battlefield tapped at the beginning of the next end step.
this.addAbility(new PrizedAmalgamTriggerdAbility(new CreateDelayedTriggeredAbilityEffect(
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true))), filter));
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true))), filter),
new CastFromGraveyardWatcher());
}
public PrizedAmalgam(final PrizedAmalgam card) {
@ -96,7 +99,20 @@ class PrizedAmalgamTriggerdAbility extends EntersBattlefieldAllTriggeredAbility
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return super.checkTrigger(event, game) && ((EntersTheBattlefieldEvent) event).getFromZone().equals(Zone.GRAVEYARD);
if (super.checkTrigger(event, game)) {
EntersTheBattlefieldEvent entersEvent = (EntersTheBattlefieldEvent) event;
if (entersEvent.getFromZone().equals(Zone.GRAVEYARD)) {
return true;
}
if (entersEvent.getFromZone().equals(Zone.STACK) && entersEvent.getTarget().getControllerId().equals(getControllerId())) {
CastFromGraveyardWatcher watcher = (CastFromGraveyardWatcher) game.getState().getWatchers().get(CastFromGraveyardWatcher.class.getName());
if (watcher != null) {
int zcc = game.getState().getZoneChangeCounter(event.getSourceId());
return watcher.spellWasCastFromGraveyard(event.getSourceId(), zcc - 1);
}
}
}
return false;
}
@Override

View file

@ -33,7 +33,6 @@ import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -64,7 +63,7 @@ public class StartledAwake extends CardImpl {
// {3}{U}{U}: Put Startled Awake from your graveyard onto the battlefield transformed. Activate this ability only any time you could cast a sorcery.
this.addAbility(new TransformAbility());
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{3}{U}{U}"));
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new StartledAwakeReturnTransformedEffect(), new ManaCostsImpl("{3}{U}{U}"));
this.addAbility(ability);
}

View file

@ -29,15 +29,12 @@ package mage.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.effects.common.counter.RemoveCounterSourceEffect;
@ -46,16 +43,10 @@ import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterSpell;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
@ -99,8 +90,6 @@ public class ThingInTheIce extends CardImpl {
ability.addEffect(effect);
this.addAbility(ability);
// When this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands.
this.addAbility(new AwokenHorrorAbility());
}
public ThingInTheIce(final ThingInTheIce card) {
@ -112,58 +101,3 @@ public class ThingInTheIce extends CardImpl {
return new ThingInTheIce(this);
}
}
class AwokenHorrorAbility extends TriggeredAbilityImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblue creatures");
static {
filter.add(Predicates.not(new SubtypePredicate("Horror")));
}
public AwokenHorrorAbility() {
super(Zone.BATTLEFIELD, new ReturnToHandFromBattlefieldAllEffect(filter), false);
// Rule only shown on the night side
this.setRuleVisible(false);
}
public AwokenHorrorAbility(final AwokenHorrorAbility ability) {
super(ability);
}
@Override
public AwokenHorrorAbility copy() {
return new AwokenHorrorAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
Permanent currentSourceObject = (Permanent) getSourceObjectIfItStillExists(game);
if (currentSourceObject != null && currentSourceObject.isNightCard()) {
return true;
}
return super.isInUseableZone(game, source, event);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands.";
}
}

View file

@ -31,14 +31,20 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.DiscardCostCardConvertedMana;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.game.Game;
import mage.target.common.TargetCreatureOrPlaneswalker;
/**
@ -55,12 +61,15 @@ public class WolfOfDevilsBreach extends CardImpl {
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Whenever Wolf of Devil's Breach attacks, you may pay {1}{R} and discard a card. If you do, Wolf of Devil's Breach deals
// Whenever Wolf of Devil's Breach attacks, you may pay {1}{R} and discard a card. If you do, Wolf of Devil's Breach deals
// damage to target creature or planeswalker equal to the discarded card's converted mana cost.
Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new DamageTargetEffect(new DiscardCostCardConvertedMana()), new ManaCostsImpl("{1}{R}")), true,
Costs toPay = new CostsImpl<>();
toPay.add(new ManaCostsImpl<>("{1}{R}"));
toPay.add(new DiscardCardCost());
Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new DamageTargetEffect(new WolfOfDevilsBreachDiscardCostCardConvertedMana()), toPay,
"Pay {1}{R} and discard a card to let {this} do damage to target creature or planeswalker equal to the discarded card's converted mana cost?", true), false,
"Whenever {this} attacks you may pay {1}{R} and discard a card. If you do, {this} deals damage to target creature or planeswalker "
+ "equal to the discarded card's converted mana cost.");
ability.addCost(new DiscardCardCost());
+ "equal to the discarded card's converted mana cost.");
ability.addTarget(new TargetCreatureOrPlaneswalker());
this.addAbility(ability);
}
@ -74,3 +83,44 @@ public class WolfOfDevilsBreach extends CardImpl {
return new WolfOfDevilsBreach(this);
}
}
class WolfOfDevilsBreachDiscardCostCardConvertedMana implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
for (Effect sourceEffect : sourceAbility.getEffects()) {
if (sourceEffect instanceof DoIfCostPaid) {
Cost doCosts = ((DoIfCostPaid) sourceEffect).getCost();
if (doCosts instanceof Costs) {
Costs costs = (Costs) doCosts;
for (Object cost : costs) {
if (cost instanceof DiscardCardCost) {
DiscardCardCost discardCost = (DiscardCardCost) cost;
int cmc = 0;
for (Card card : discardCost.getCards()) {
cmc += card.getManaCost().convertedManaCost();
}
return cmc;
}
}
}
}
}
return 0;
}
@Override
public WolfOfDevilsBreachDiscardCostCardConvertedMana copy() {
return new WolfOfDevilsBreachDiscardCostCardConvertedMana();
}
@Override
public String toString() {
return "";
}
@Override
public String getMessage() {
return "the discarded card's converted mana cost";
}
}

View file

@ -30,7 +30,7 @@ package mage.sets.urzasdestiny;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.condition.common.EnchantedCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
@ -76,7 +76,7 @@ public class RayneAcademyChancellor extends CardImpl {
class RayneAcademyChancellorTriggeredAbility extends TriggeredAbilityImpl {
RayneAcademyChancellorTriggeredAbility() {
super(Zone.BATTLEFIELD, new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(2), new DrawCardSourceControllerEffect(1), new EnchantedCondition(), "you may draw a card. You may draw an additional card if {this} is enchanted."), true);
super(Zone.BATTLEFIELD, new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(2), new DrawCardSourceControllerEffect(1), new EnchantedSourceCondition(), "you may draw a card. You may draw an additional card if {this} is enchanted."), true);
}
RayneAcademyChancellorTriggeredAbility(final RayneAcademyChancellorTriggeredAbility ability) {

View file

@ -27,6 +27,8 @@
*/
package mage.sets.visions;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -36,10 +38,8 @@ import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.constants.ZoneDetail;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetSpell;
@ -70,14 +70,6 @@ public class Desertion extends CardImpl {
class DesertionEffect extends OneShotEffect {
private static final FilterSpell filter = new FilterSpell("artifact or creature spell");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE)));
}
public DesertionEffect() {
super(Outcome.Detriment);
this.staticText = "Counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard.";
@ -96,7 +88,18 @@ class DesertionEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.BATTLEFIELD, false, ZoneDetail.NONE);
Spell targetSpell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (targetSpell != null) {
Set<CardType> cardTypes = new HashSet<>(targetSpell.getCardType());
if (!cardTypes.isEmpty()) {
//targetPointer.getFirst(game, source)
if (cardTypes.contains(CardType.ARTIFACT) || cardTypes.contains(CardType.CREATURE)) {
return game.getStack().counter(targetSpell.getId(), source.getSourceId(), game, Zone.BATTLEFIELD, false, ZoneDetail.NONE);
} else {
return game.getStack().counter(targetSpell.getId(), source.getSourceId(), game);
}
}
}
}
return false;
}

View file

@ -33,7 +33,7 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.EquippedCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -61,9 +61,9 @@ public class KitesailApprentice extends CardImpl {
this.power = new MageInt(1);
this.toughness = new MageInt(1);
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedCondition.getInstance(), rule1);
ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), EquippedSourceCondition.getInstance(), rule1);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect1));
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance()), EquippedCondition.getInstance(), rule2);
ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance()), EquippedSourceCondition.getInstance(), rule2);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2));
}

View file

@ -27,9 +27,12 @@
*/
package org.mage.test.cards.abilities.keywords;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -152,7 +155,62 @@ public class TransformTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Autumnal Gloom", 0);
assertPermanentCount(playerA, "Ancient of the Equinox", 1);
}
/**
* 4G Creature - Human Shaman Whenever a permanent you control transforms
* into a non-Human creature, put a 2/2 green Wolf creature token onto the
* battlefield.
*
* Reported bug: "It appears to trigger either when a non-human creature
* transforms OR when a creature transforms from a non-human into a human
* (as in when a werewolf flips back to the sun side), rather than when a
* creature transforms into a non-human, as is the intended function and
* wording of the card."
*/
@Test
public void testCultOfTheWaxingMoon() {
// Whenever a permanent you control transforms into a non-Human creature, put a 2/2 green Wolf creature token onto the battlefield.
addCard(Zone.BATTLEFIELD, playerA, "Cult of the Waxing Moon");
// {1}{G} - Human Werewolf
// At the beginning of each upkeep, if no spells were cast last turn, transform Hinterland Logger.
addCard(Zone.BATTLEFIELD, playerA, "Hinterland Logger");
// At the beginning of each upkeep, if a player cast two or more spells last turn, transform Timber Shredder.
setStopAt(2, PhaseStep.DRAW);
execute();
assertPermanentCount(playerA, "Cult of the Waxing Moon", 1);
assertPermanentCount(playerA, "Timber Shredder", 1); // Night-side card of Hinterland Logger, Werewolf (non-human)
assertPermanentCount(playerA, "Wolf", 1); // wolf token created
}
/**
* Yeah, it sounds like the same thing. When Startled Awake is in the
* graveyard, you can pay CMC 5 to return it, flipped, to the battlefield as
* a 1/1 creature. However, after paying the 5 it returns unflipped and just
* stays on the battlefield as a sorcery, of which it can't be interacted
* with at all wording of the card."
*/
@Test
public void testStartledAwake() {
// Target opponent puts the top thirteen cards of his or her library into his or her graveyard.
// {3}{U}{U}: Put Startled Awake from your graveyard onto the battlefield transformed. Activate this ability only any time you could cast a sorcery.
addCard(Zone.HAND, playerA, "Startled Awake"); // SORCERY {2}{U}{U}"
addCard(Zone.BATTLEFIELD, playerA, "Island", 9);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}{U}");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 13);
assertGraveyardCount(playerA, "Startled Awake", 0);
assertPermanentCount(playerA, "Persistent Nightmare", 1); // Night-side card of Startled Awake
Permanent nightmare = getPermanent("Persistent Nightmare", playerA);
Assert.assertTrue("Has to have creature card type", nightmare.getCardType().contains(CardType.CREATURE));
Assert.assertFalse("Has not to have sorcery card type", nightmare.getCardType().contains(CardType.SORCERY));
}
}

View file

@ -88,6 +88,38 @@ public class CopySpellTest extends CardTestPlayerBase {
assertPowerToughness(playerB, "Silvercoat Lion", 2, 2);
assertAbility(playerB, "Silvercoat Lion", FlyingAbility.getInstance(), false);
}
/**
* Reported bug: "Silverfur Partisan and fellow wolves did not trigger off of copies of Strength of Arms made by Zada, Hedron Grinder.
* Not sure about other spells, but I imagine similar results."
*/
@Test
public void ZadaHedronSilverfurPartisan() {
// {2}{G}
// Trample
// Whenever a Wolf or Werewolf you control becomes the target of an instant or sorcery spell, put a 2/2 green Wolf creature token onto the battlefield.
addCard(Zone.BATTLEFIELD, playerA, "Silverfur Partisan"); // 2/2 Wolf Warrior
// Whenever you cast an instant or sorcery spell that targets only Zada, Hedron Grinder, copy that spell for each other creature you control that the spell could target. Each copy targets a different one of those creatures.
addCard(Zone.BATTLEFIELD, playerA, "Zada, Hedron Grinder", 1);
// Target creature gets +3/+3 until end of turn.
addCard(Zone.HAND, playerA, "Giant Growth", 1); // {G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
//castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Village Messenger");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Zada, Hedron Grinder");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Giant Growth", 1);
assertPowerToughness(playerA, "Silverfur Partisan", 5, 5);
assertPowerToughness(playerA, "Zada, Hedron Grinder", 6, 6);
assertPermanentCount(playerA, "Wolf", 1); // created from Silverfur ability
}
@Test
public void ZadaHedronGrinderBoostWithCharm() {
@ -172,5 +204,36 @@ public class CopySpellTest extends CardTestPlayerBase {
assertHandCount(playerA, "Evermind", 1);
assertHandCount(playerA, 3); // Evermind + 1 card from Evermind spliced on cast Into the fray and 1 from the copied spell with splice
}
/**
* {4}{U} Enchantment (Enchant Player)
* Whenever enchanted player casts an instant or sorcery spell, each other player may copy that spell
* and may choose new targets for the copy he or she controls.
*
* Reported bug: "A player with Curse of Echoes attached to them played Bribery and the player who controlled the curse had control
* of all 3 copies. This seems to be the case for all spells."
*/
@Test
public void testCurseOfEchoes() {
addCard(Zone.HAND, playerA, "Curse of Echoes");
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
addCard(Zone.HAND, playerB, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Echoes");
addTarget(playerA, playerB);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt");
addTarget(playerB, playerA); // original target
setChoice(playerA, "Yes");
addTarget(playerA, playerB);
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, "Lightning Bolt", 1);
assertLife(playerA, 17); // still takes original spell's damage
assertLife(playerB, 17); // copy redirected
}
}

View file

@ -19,11 +19,11 @@ public class WerewolfRansackerTest extends CardTestPlayerBase {
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 17);
assertPermanentCount(playerB, "Ornithopter", 0);
assertPermanentCount(playerA, "Afflicted Deserter", 0);
assertPermanentCount(playerA, "Werewolf Ransacker", 1);
assertLife(playerA, 20);
assertLife(playerB, 17);
}
@Test
@ -69,5 +69,5 @@ public class WerewolfRansackerTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Ornithopter", 0);
assertPermanentCount(playerA, "Afflicted Deserter", 1);
assertPermanentCount(playerA, "Werewolf Ransacker", 0);
}
}
}

View file

@ -0,0 +1,60 @@
package org.mage.test.cards.single.soi;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* {3}{W}{W} Angel - day
*
* Flash Flying, vigilance When Archangel Avacyn enters the battlefield,
* creatures you control gain indestructible until end of turn. When a non-Angel
* creature you control dies, transform Archangel Avacyn at the beginning of the
* next upkeep.
*
* (Night) - red card When this creature transforms into Avacyn, the Purifier,
* it deals 3 damage to each other creature and each opponent.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class ArchangelAvacynTest extends CardTestPlayerBase {
/**
* Reported bug: "Archangel Avacyn damages herself when she transforms"
*/
@Test
public void basicTransformTest() {
// Flash
// Flying
// Vigilance
// When Archangel Avacyn enters the battlefield, creatures you control gain indestructible until end of turn.
// When a non-Angel creature you control dies, transform Archangel Avacyn at the beginning of the next upkeep.
addCard(Zone.BATTLEFIELD, playerA, "Archangel Avacyn");
addCard(Zone.BATTLEFIELD, playerA, "Wall of Omens"); // 0/4
addCard(Zone.HAND, playerA, "Elite Vanguard"); // 2/1
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // 3/1
addCard(Zone.BATTLEFIELD, playerB, "Wall of Roots"); // 0/5
addCard(Zone.HAND, playerB, "Shock");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock");
addTarget(playerB, "Elite Vanguard");
setStopAt(3, PhaseStep.DRAW);
execute();
assertPermanentCount(playerA, "Avacyn, the Purifier", 1);
assertPermanentCount(playerA, "Wall of Omens", 1);
assertGraveyardCount(playerA, "Elite Vanguard", 1);
assertPermanentCount(playerB, "Wall of Roots", 1);
assertGraveyardCount(playerB, "Hill Giant", 1);
assertGraveyardCount(playerB, "Shock", 1);
Permanent avacyn = getPermanent("Avacyn, the Purifier", playerA);
Assert.assertEquals("Damage to Avacyn, the Purifier should be 0 not 3", 0, avacyn.getDamage());
}
}

View file

@ -0,0 +1,78 @@
package org.mage.test.cards.single.soi;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author halljared
*/
public class ErdwalIlluminatorTest extends CardTestPlayerBase {
/**
* Whenever you investigate for the first time each turn, investigate an additional time.
*/
@Test
public void investigateFirstTimeTriggers() {
addCard(Zone.HAND, playerA, "Thraben Inspector", 1);
addCard(Zone.BATTLEFIELD, playerA, "Erdwal Illuminator", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thraben Inspector");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Clue", 2);
}
@Test
public void ignoresOpponentInvestigateTriggers() {
addCard(Zone.HAND, playerB, "Thraben Inspector", 1);
addCard(Zone.BATTLEFIELD, playerA, "Erdwal Illuminator", 1);
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Thraben Inspector");
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerB, "Clue", 1);
}
@Test
public void ignoresSecondInvestigateTriggers() {
addCard(Zone.HAND, playerA, "Thraben Inspector", 2);
addCard(Zone.BATTLEFIELD, playerA, "Erdwal Illuminator", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thraben Inspector");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thraben Inspector");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Clue", 3);
}
@Test
public void separateTurnsInvestigateTriggers() {
addCard(Zone.HAND, playerA, "Thraben Inspector", 2);
addCard(Zone.BATTLEFIELD, playerA, "Erdwal Illuminator", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thraben Inspector");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Thraben Inspector");
setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Clue", 4);
}
}

View file

@ -0,0 +1,101 @@
package org.mage.test.cards.single.soi;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* 4RR
* Creature - Angel
* Flying
*
* Whenever a source an opponent controls deals damage to you or a permanent you control,
* you may have Flameblade Angel deal 1 damage to that source's controller.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class FlamebladeAngelTest extends CardTestPlayerBase {
/**
* Reported bug: Not triggering when damage is dealt to the creatures I control.
*/
@Test
public void testDamageToCreature() {
addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel");
addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5
addCard(Zone.HAND, playerB, "Shock"); // instant deals 2 dmg to creature/player
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock");
addTarget(playerB, "Wall of Roots");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
Permanent roots = getPermanent("Wall of Roots", playerA);
Assert.assertEquals("Wall of Roots should have 2 damage dealt to it", 2, roots.getDamage());
assertGraveyardCount(playerB, "Shock", 1);
assertLife(playerA, 20);
assertLife(playerB, 19); // Angel should deal 1 damage to Shock's controller
}
/**
* Reported bug: Not triggering when damage is dealt to the creatures I control.
*/
@Test
public void testDamageToMultipleCreatures() {
addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel");
addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2
addCard(Zone.HAND, playerB, "Shock", 2); // instant deals 2 dmg to creature/player
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock");
addTarget(playerB, "Wall of Roots");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock");
addTarget(playerB, "Grizzly Bears");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
Permanent roots = getPermanent("Wall of Roots", playerA);
Assert.assertEquals("Wall of Roots should have 2 damage dealt to it", 2, roots.getDamage());
assertGraveyardCount(playerB, "Shock", 2);
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertLife(playerA, 20);
assertLife(playerB, 18); // Angel should deal 1 damage twice to Shock's controller
}
/**
* Reported bug: Not triggering when damage is dealt to the creatures I control.
*/
@Test
public void testCombatDamageToCreatures() {
addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel");
addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2
addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); // 2/1
addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // 3/3
attack(2, playerB, "Elite Vanguard");
attack(2, playerB, "Hill Giant");
block(2, playerA, "Wall of Roots", "Hill Giant");
block(2, playerA, "Grizzly Bears", "Elite Vanguard");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
Permanent roots = getPermanent("Wall of Roots", playerA);
Assert.assertEquals("Wall of Roots should have 3 damage dealt to it", 3, roots.getDamage());
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertGraveyardCount(playerB, "Elite Vanguard", 1);
assertLife(playerA, 20);
assertLife(playerB, 18); // Angel should deal 1 damage twice
}
}

View file

@ -6,98 +6,103 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*1UB
* Creature - Zombie
* Whenever a creature enters the battlefield, if it entered from your graveyard or you cast it from your graveyard,
* return Prized Amalgam from your graveyard to the battlefield tapped at the beginning of the next end step.
* 1UB Creature - Zombie Whenever a creature enters the battlefield, if it
* entered from your graveyard or you cast it from your graveyard, return Prized
* Amalgam from your graveyard to the battlefield tapped at the beginning of the
* next end step.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class PrizedAmalgamTest extends CardTestPlayerBase {
/**
* Reanimated creature recurs Prized Amalgam
*/
@Test
public void testReanimation() {
addCard(Zone.HAND, playerA, "Reanimate", 1); // {B} Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.
addCard(Zone.GRAVEYARD, playerA, "Bronze Sable", 1); // (2) 2/1
addCard(Zone.GRAVEYARD, playerA, "Prized Amalgam", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate");
addTarget(playerA, "Bronze Sable");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerA, 18); // loss of 2 from reanimate
assertGraveyardCount(playerA, "Reanimate", 1);
assertPermanentCount(playerA, "Bronze Sable", 1);
assertPermanentCount(playerA, "Prized Amalgam", 1);
assertTapped("Prized Amalgam", true);
}
/**
* Reported bug - Gravecrawler cast from grave does not trigger Prized Amalgam.
* Reported bug - Gravecrawler cast from grave does not trigger Prized
* Amalgam.
*/
@Test
public void testGravecrawlerCastFromGrave() {
addCard(Zone.GRAVEYARD, playerA, "Gravecrawler", 1);
addCard(Zone.GRAVEYARD, playerA, "Prized Amalgam", 1);
addCard(Zone.BATTLEFIELD, playerA, "Gnawing Zombie", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gravecrawler");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Gravecrawler", 1);
assertPermanentCount(playerA, "Prized Amalgam", 1);
assertTapped("Prized Amalgam", true);
assertTapped("Prized Amalgam", true);
}
/**
* Reported bug - creature returned from opponent's graveyard to battlefield by Ojutai's Command incorrectly triggers Prized Amalgam
* Reported bug - creature returned from opponent's graveyard to battlefield
* by Ojutai's Command incorrectly triggers Prized Amalgam
*/
@Test
public void testOpponentReturnsCreatureFromGrave() {
addCard(Zone.HAND, playerA, "Reanimate", 1);
addCard(Zone.GRAVEYARD, playerA, "Hill Giant", 1); // {3}{R} 3/3
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.GRAVEYARD, playerB, "Prized Amalgam", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate");
addTarget(playerA, "Hill Giant");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerA, 16); // lose 4 life from reanimate 4 CMC
assertPermanentCount(playerA, "Hill Giant", 1);
assertPermanentCount(playerB, "Prized Amalgam", 0); // should not recur
assertGraveyardCount(playerB, "Prized Amalgam", 1); // stays in grave
}
/*
/*
* Test opponent returning a card from your graveyard to battlefield.
*/
*/
@Test
public void testOpponentReturnsCreatureFromYourGrave() {
addCard(Zone.HAND, playerA, "Necromantic Summons", 1); // Put target creature card from a graveyard onto the battlefield under your control.
addCard(Zone.HAND, playerA, "Necromantic Summons", 1); // Put target creature card from a graveyard onto the battlefield under your control.
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
addCard(Zone.GRAVEYARD, playerB, "Merfolk Looter", 1); // {U} 1/1
addCard(Zone.GRAVEYARD, playerB, "Merfolk Looter", 1); // {U} 1/1
// Whenever a creature enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, return Prized Amalgam from your graveyard to the battlefield tapped at the beginning of the next end step.
addCard(Zone.GRAVEYARD, playerB, "Prized Amalgam", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromantic Summons");
addTarget(playerA, "Merfolk Looter");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Merfolk Looter", 1);
assertPermanentCount(playerB, "Prized Amalgam", 0); // should not recur
assertGraveyardCount(playerB, "Prized Amalgam", 1); // stays in grave
assertPermanentCount(playerB, "Prized Amalgam", 1);
assertGraveyardCount(playerB, "Prized Amalgam", 0);
assertTapped("Prized Amalgam", true);
}
}

View file

@ -1,75 +1,71 @@
package org.mage.test.cards.single.soi;
import java.util.Set;
import mage.cards.Card;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* 3BG
Legendary Creature - Frog Horror
Deathtouch
At the beginning of your upkeep, sacrifice The Gitrog Monster unless you sacrifice a land.
You may play an additional land on each of your turns.
Whenever one or more land cards are put into your graveyard from anywhere, draw a card.
* 3BG Legendary Creature - Frog Horror Deathtouch
*
* At the beginning of your upkeep, sacrifice The Gitrog Monster unless you
* sacrifice a land.
*
* You may play an additional land on each of your turns.
*
* Whenever one or more land cards are put into your graveyard from anywhere,
* draw a card.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class TheGitrogMonsterTest extends CardTestPlayerBase {
public class TheGitrogMonsterTest extends CardTestPlayerBase {
/**
* Basic sacrifice test when no lands are present
*/
@Test
public void noLandsSacrificeGitrog() {
addCard(Zone.HAND, playerA, "The Gitrog Monster", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.HAND, playerB, "Armageddon", 1); // destroy all lands
addCard(Zone.BATTLEFIELD, playerB, "Plains", 4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Gitrog Monster");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Armageddon");
setStopAt(3, PhaseStep.DRAW);
execute();
Set<Card> hand = playerA.getHand().getCards(currentGame);
assertGraveyardCount(playerA, "Swamp", 3);
assertGraveyardCount(playerA, "Forest", 2);
assertGraveyardCount(playerA, "The Gitrog Monster", 1);
assertGraveyardCount(playerB, "Plains", 4);
assertGraveyardCount(playerB, "Armageddon", 1);
assertPermanentCount(playerA, "The Gitrog Monster", 0);
assertHandCount(playerA, 6); // 1 for turn, 5 more for lands that hit the grave
assertHandCount(playerA, 2); // 1 for turn, 1 more for lands that hit the grave
}
/**
* Basic sacrifice test when there is a land
*/
@Test
public void hasLandsSacrificeLand() {
addCard(Zone.HAND, playerA, "The Gitrog Monster", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Gitrog Monster");
// on 3rd turn during upkeep opt to sacrifice a land
setChoice(playerA, "Yes");
addTarget(playerA, "Swamp");
setStopAt(3, PhaseStep.DRAW);
execute();
Set<Card> hand = playerA.getHand().getCards(currentGame);
assertGraveyardCount(playerA, "Swamp", 1);
assertPermanentCount(playerA, "The Gitrog Monster", 1);
assertHandCount(playerA, 2); // 1 for turn, 1 more for land sacrificed

View file

@ -0,0 +1,62 @@
package org.mage.test.cards.single.soi;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Defender
*
* Thing in the Ice enters the battlefield with four ice counters on it.
*
* Whenever you cast an instant or sorcery spell, remove an ice counter from
* Thing in the Ice. Then if it has no ice counters on it, transform it. When
* this creature transforms into Awoken Horror, return all non-Horror creatures
* to their owners' hands.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class ThingInTheIceTest extends CardTestPlayerBase {
/**
* Reported bug: When Thing in the Ice transforms, it bounces Clue tokens.
*
*/
@Test
public void testClueTokens() {
// Whenever a land enters the battlefield under your control, investigate. <i>(Put a colorless Clue artifact token onto the battlefield with "{2}, Sacrifice this artifact: Draw a card.")</i>
// Whenever you sacrifice a Clue, put a +1/+1 counter on Tireless Tracker.
addCard(Zone.BATTLEFIELD, playerA, "Tireless Tracker", 1); // Human, Scout 3/2
addCard(Zone.HAND, playerA, "Forest", 1);
// Defender
// Thing in the Ice enters the battlefield with four ice counters on it.
// Whenever you cast an instant or sorcery spell, remove an ice counter from Thing in the Ice. Then if it has no ice counters on it, transform it.
// When this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands.
addCard(Zone.BATTLEFIELD, playerB, "Thing in the Ice", 1);
// Target creature gains haste until end of turn.
// Draw a card.
addCard(Zone.HAND, playerB, "Expedite", 4);
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest"); // creates a clue
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Expedite");
addTarget(playerB, "Thing in the Ice");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Expedite");
addTarget(playerB, "Thing in the Ice");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Expedite");
addTarget(playerB, "Thing in the Ice");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Expedite");
addTarget(playerB, "Thing in the Ice"); // remove all 4 ice counters to transform it
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Clue", 1);
assertHandCount(playerA, "Tireless Tracker", 1); // returned to hand
assertPermanentCount(playerA, "Tireless Tracker", 0);
assertPermanentCount(playerB, "Awoken Horror", 1); // transformed
assertGraveyardCount(playerB, "Expedite", 4);
}
}

View file

@ -6,54 +6,54 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* 3RR
Creature - Elemental Wolf
*Whenever Wolf of Devil's Breach attacks, you may pay 1R and discard a card.
* If you do, Wolf of Devil's Breach deals damage to target creature or planeswalker equal to the discarded card's converted mana cost.
* 3RR Creature - Elemental Wolf Whenever Wolf of Devil's Breach attacks, you
* may pay 1R and discard a card. If you do, Wolf of Devil's Breach deals damage
* to target creature or planeswalker equal to the discarded card's converted
* mana cost.
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class WolfOfDevilsBreachTest extends CardTestPlayerBase {
/**
*
*/
@Test
public void attackChooseToPay() {
// Whenever Wolf of Devil's Breach attacks, you may pay {1}{R} and discard a card. If you do, Wolf of Devil's Breach deals
// damage to target creature or planeswalker equal to the discarded card's converted mana cost.
addCard(Zone.BATTLEFIELD, playerA, "Wolf of Devil's Breach", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Bronze Sable", 1); // (2) 2/1
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); // 2/2
attack(1, playerA, "Wolf of Devil's Breach");
setChoice(playerA, "Yes");
setChoice(playerA, "Bronze Sable");
addTarget(playerA, "Grizzly Bears");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertGraveyardCount(playerA, "Bronze Sable", 1);
assertGraveyardCount(playerB, "Grizzly Bears", 1);
}
/**
*
*/
@Test
public void attackDoNotPay() {
// Whenever Wolf of Devil's Breach attacks, you may pay {1}{R} and discard a card. If you do, Wolf of Devil's Breach deals
// damage to target creature or planeswalker equal to the discarded card's converted mana cost.
addCard(Zone.BATTLEFIELD, playerA, "Wolf of Devil's Breach", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Bronze Sable", 1); // (2) 2/1
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); // 2/2
attack(1, playerA, "Wolf of Devil's Breach");
setChoice(playerA, "No");
setChoice(playerA, "Bronze Sable");
addTarget(playerA, "Grizzly Bears");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertHandCount(playerA, "Bronze Sable", 1); // never discarded
assertGraveyardCount(playerA, "Bronze Sable", 0);
assertGraveyardCount(playerB, "Grizzly Bears", 0);

View file

@ -36,7 +36,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
*
* @author LevelX2
*/
public class SpellCastTriggerTest extends CardTestPlayerBase {
/**
@ -50,7 +49,7 @@ public class SpellCastTriggerTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Sunscorch Regent", 1);
addCard(Zone.HAND, playerB, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
@ -59,9 +58,9 @@ public class SpellCastTriggerTest extends CardTestPlayerBase {
assertLife(playerA, 18); // 20 -3 +1
assertLife(playerB, 20);
assertGraveyardCount(playerB, "Lightning Bolt", 1);
assertPowerToughness(playerA, "Sunscorch Regent", 5, 4);
}
@ -69,13 +68,13 @@ public class SpellCastTriggerTest extends CardTestPlayerBase {
* Monastery Mentor triggers are causing a "rollback" error.
*/
@Test
public void testMonasteryMentor() {
public void testMonasteryMentor() {
// Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)
// Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield.
addCard(Zone.BATTLEFIELD, playerA, "Monastery Mentor", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
@ -83,14 +82,67 @@ public class SpellCastTriggerTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerA, 20);
assertLife(playerA, 20);
assertLife(playerB, 14);
assertGraveyardCount(playerA, "Lightning Bolt", 2);
assertPermanentCount(playerA, "Monk", 2);
assertPowerToughness(playerA, "Monk", 2, 2);
assertPowerToughness(playerA, "Monk", 1, 1);
assertPowerToughness(playerA, "Monastery Mentor", 4, 4);
}
}
@Test
public void testHarnessTheStormFirstTurn() {
// Whenever you cast an instant or sorcery spell from your hand, you may cast target card with the same name as that spell from your graveyard.
addCard(Zone.BATTLEFIELD, playerA, "Harness the Storm", 1);
// Put two 1/1 red Devil creature tokens onto the battlefield. They have "When this creature dies, it deals 1 damage to target creature or player."
addCard(Zone.HAND, playerA, "Dance with Devils", 1); // {3}{R}
addCard(Zone.GRAVEYARD, playerA, "Dance with Devils", 1); // {3}{R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dance with Devils");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Dance with Devils", 2);
assertPermanentCount(playerA, "Devil", 4);
}
/**
* I had cast Dance with Devils the turn before. On this turn I was casting
* Read the Bones. The enchantment should not have triggered and if it did
* it should have asked me to cast read the bones.
*/
@Test
public void testHarnessTheStormThirdTurn() {
// Whenever you cast an instant or sorcery spell from your hand, you may cast target card with the same name as that spell from your graveyard.
addCard(Zone.BATTLEFIELD, playerA, "Harness the Storm", 1);
// Put two 1/1 red Devil creature tokens onto the battlefield. They have "When this creature dies, it deals 1 damage to target creature or player."
addCard(Zone.HAND, playerA, "Dance with Devils", 1); // {3}{R}
// Scry 2, then draw two cards. You lose 2 life.
addCard(Zone.HAND, playerA, "Read the Bones", 1); // {2}{B}
addCard(Zone.GRAVEYARD, playerA, "Read the Bones", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dance with Devils");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Read the Bones");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Dance with Devils", 1);
assertPermanentCount(playerA, "Devil", 2);
assertGraveyardCount(playerA, "Read the Bones", 2);
assertHandCount(playerA, 5); // one normally drawn + 4 from Read the Bones
}
}

View file

@ -0,0 +1,155 @@
package org.mage.test.multiplayer;
import java.io.FileNotFoundException;
import mage.constants.MultiplayerAttackOption;
import mage.constants.PhaseStep;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.game.FreeForAll;
import mage.game.Game;
import mage.game.GameException;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
/**
* Enchantment {3}{B}
* At the beginning of your upkeep, each player discards a card.
* Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.
* (Players reveal the discarded cards simultaneously.)
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com
*/
public class CreepingDreadTest extends CardTestMultiPlayerBase {
@Override
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, 0, 40);
// Player order: A -> D -> C -> B
playerA = createPlayer(game, playerA, "PlayerA");
playerB = createPlayer(game, playerB, "PlayerB");
playerC = createPlayer(game, playerC, "PlayerC");
playerD = createPlayer(game, playerD, "PlayerD");
return game;
}
/**
* Discard creature and all opponents who discard creature lose 3 life
*/
@Test
public void basicTest() {
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
addCard(Zone.HAND, playerA, "Merfolk Looter");
addCard(Zone.HAND, playerB, "Hill Giant");
addCard(Zone.HAND, playerC, "Elite Vanguard");
addCard(Zone.HAND, playerD, "Bone Saw");
setChoice(playerA, "Merfolk Looter");
setChoice(playerB, "Hill Giant");
setChoice(playerC, "Elite Vanguard");
setChoice(playerD, "Bone Saw");
setStopAt(1, PhaseStep.DRAW);
execute();
assertGraveyardCount(playerA, "Merfolk Looter", 1);
assertGraveyardCount(playerB, "Hill Giant", 1);
assertGraveyardCount(playerC, "Elite Vanguard", 1);
assertGraveyardCount(playerD, "Bone Saw", 1);
assertLife(playerA, 40); // 4-player commander so 40 life starting
assertLife(playerB, 37); // matched creature type
assertLife(playerC, 37);
assertLife(playerD, 40); // no match
}
/**
* Discard Artifact Creature and all opponents who discard either an Artifact or Creature lose 3 life
*/
@Test
public void twoTypesTest() {
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
addCard(Zone.HAND, playerA, "Bronze Sable");
addCard(Zone.HAND, playerB, "Hill Giant");
addCard(Zone.HAND, playerC, "Swamp");
addCard(Zone.HAND, playerD, "Bone Saw");
setChoice(playerA, "Bronze Sable");
setChoice(playerB, "Hill Giant");
setChoice(playerC, "Swamp");
setChoice(playerD, "Bone Saw");
setStopAt(1, PhaseStep.DRAW);
execute();
assertGraveyardCount(playerA, "Bronze Sable", 1);
assertGraveyardCount(playerB, "Hill Giant", 1);
assertGraveyardCount(playerC, "Swamp", 1);
assertGraveyardCount(playerD, "Bone Saw", 1);
assertLife(playerA, 40); // artifact-creature discarded
assertLife(playerB, 37); // creature
assertLife(playerC, 40); // neither
assertLife(playerD, 37); // artifact
}
/**
* Discard enchantment and no opponents discard an enchantment, so no one loses life
*/
@Test
public void noMatchesTest() {
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
addCard(Zone.HAND, playerA, "Moat"); // enchantment
addCard(Zone.HAND, playerB, "Hill Giant");
addCard(Zone.HAND, playerC, "Swamp");
addCard(Zone.HAND, playerD, "Bone Saw");
setChoice(playerA, "Moat");
setChoice(playerB, "Hill Giant");
setChoice(playerC, "Swamp");
setChoice(playerD, "Bone Saw");
setStopAt(1, PhaseStep.DRAW);
execute();
assertGraveyardCount(playerA, "Moat", 1);
assertGraveyardCount(playerB, "Hill Giant", 1);
assertGraveyardCount(playerC, "Swamp", 1);
assertGraveyardCount(playerD, "Bone Saw", 1);
assertLife(playerA, 40); // no matches
assertLife(playerB, 40);
assertLife(playerC, 40);
assertLife(playerD, 40);
}
/**
* Upkeep player has no cards to discard, so no matches
*/
@Test
public void noDiscardNoMatches() {
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
addCard(Zone.HAND, playerB, "Hill Giant");
addCard(Zone.HAND, playerC, "Swamp");
addCard(Zone.HAND, playerD, "Bone Saw");
setChoice(playerB, "Hill Giant");
setChoice(playerC, "Swamp");
setChoice(playerD, "Bone Saw");
setStopAt(1, PhaseStep.DRAW);
execute();
assertGraveyardCount(playerB, "Hill Giant", 1);
assertGraveyardCount(playerC, "Swamp", 1);
assertGraveyardCount(playerD, "Bone Saw", 1);
assertLife(playerA, 40); // no matches
assertLife(playerB, 40);
assertLife(playerC, 40);
assertLife(playerD, 40);
}
}

View file

@ -1167,6 +1167,9 @@ public abstract class AbilityImpl implements Ability {
public MageObject getSourceObjectIfItStillExists(Game game) {
MageObject currentObject = game.getObject(getSourceId());
if (currentObject != null) {
if (sourceObject == null) {
setSourceObject(currentObject, game);
}
MageObjectReference mor = new MageObjectReference(currentObject, game);
if (mor.getZoneChangeCounter() == getSourceObjectZoneChangeCounter()) {
// source object has meanwhile not changed zone

View file

@ -156,7 +156,7 @@ public class BeginningOfEndStepTriggeredAbility extends TriggeredAbilityImpl {
private String generateConditionString() {
if (interveningIfClauseCondition != null) {
return new StringBuilder(interveningIfClauseCondition.toString()).append(", ").toString();
return "if {this} is " + interveningIfClauseCondition.toString() + ", ";
}
switch (getZone()) {
case GRAVEYARD:

View file

@ -28,9 +28,9 @@
package mage.abilities.condition.common;
import java.util.UUID;
import mage.constants.CardType;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -38,15 +38,15 @@ import mage.game.permanent.Permanent;
*
* @author North
*/
public class EnchantedCondition implements Condition {
public class EnchantedSourceCondition implements Condition {
private int numberOfEnchantments;
public EnchantedCondition() {
public EnchantedSourceCondition() {
this(1);
}
public EnchantedCondition(int numberOfEnchantments) {
public EnchantedSourceCondition(int numberOfEnchantments) {
this.numberOfEnchantments = numberOfEnchantments;
}
@ -58,7 +58,7 @@ public class EnchantedCondition implements Condition {
for (UUID uuid : permanent.getAttachments()) {
Permanent attached = game.getBattlefield().getPermanent(uuid);
if (attached != null && attached.getCardType().contains(CardType.ENCHANTMENT)) {
if (++numberOfFoundEnchantments >= numberOfEnchantments) {
if (++numberOfFoundEnchantments >= numberOfEnchantments) {
return true;
}
}
@ -66,4 +66,9 @@ public class EnchantedCondition implements Condition {
}
return (numberOfFoundEnchantments >= numberOfEnchantments);
}
@Override
public String toString() {
return "enchanted";
}
}

View file

@ -38,9 +38,9 @@ import mage.game.permanent.Permanent;
*
* @author nantuko
*/
public class EquippedCondition implements Condition {
public class EquippedSourceCondition implements Condition {
private static final EquippedCondition fInstance = new EquippedCondition();
private static final EquippedSourceCondition fInstance = new EquippedSourceCondition();
public static Condition getInstance() {
return fInstance;
@ -59,4 +59,10 @@ public class EquippedCondition implements Condition {
}
return false;
}
@Override
public String toString() {
return "equipped";
}
}

View file

@ -67,7 +67,7 @@ public class DiscardTargetCost extends CostImpl {
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
this.cards.clear();
this.targets.clearChosen();;
this.targets.clearChosen();
Player player = game.getPlayer(controllerId);
if (player == null) {
return false;

View file

@ -32,6 +32,7 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.Duration;
@ -90,6 +91,10 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
UUID sourceId = event.getSourceId();
UUID controllerId = event.getPlayerId();
if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null) {
card = card.getSecondCardFace();
}
// Aura cards that go to battlefield face down (Manifest) don't have to select targets
if (card.isFaceDown(game)) {
return false;
@ -167,6 +172,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
}
Player targetPlayer = game.getPlayer(targetId);
if (targetCard != null || targetPermanent != null || targetPlayer != null) {
card = game.getCard(event.getTargetId());
card.removeFromZone(game, fromZone, sourceId);
card.updateZoneChangeCounter(game);
PermanentCard permanent = new PermanentCard(card, (controllingPlayer == null ? card.getOwnerId() : controllingPlayer.getId()), game);
@ -200,7 +206,12 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD)
&& !(((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK))) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) {
if (card != null && (card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")
|| // in case of transformable enchantments
(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null
&& card.getSecondCardFace() != null
&& card.getSecondCardFace().getCardType().contains(CardType.ENCHANTMENT)
&& card.getSecondCardFace().hasSubtype("Aura")))) {
return true;
}
}

View file

@ -90,6 +90,10 @@ public class DoIfCostPaid extends OneShotEffect {
return game.getPlayer(source.getControllerId());
}
public Cost getCost() {
return cost;
}
@Override
public String getText(Mode mode) {
if (!staticText.isEmpty()) {

View file

@ -31,6 +31,7 @@ import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -78,20 +79,30 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = null;
Cards cardsToMove = new CardsImpl();
if (fromExileZone) {
UUID exilZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (exilZoneId != null) {
ExileZone exileZone = game.getExile().getExileZone(exilZoneId);
if (exileZone != null && getTargetPointer().getFirst(game, source) != null) {
card = exileZone.get(getTargetPointer().getFirst(game, source), game);
if (exileZone != null) {
for (UUID cardId : getTargetPointer().getTargets(game, source)) {
Card card = exileZone.get(cardId, game);
if (card != null) {
cardsToMove.add(card);
}
}
}
}
} else {
card = game.getCard(getTargetPointer().getFirst(game, source));
for (UUID cardId : getTargetPointer().getTargets(game, source)) {
Card card = game.getCard(cardId);
if (card != null) {
cardsToMove.add(card);
}
}
}
if (card != null) {
controller.moveCards(new CardsImpl(card).getCards(game),
if (!cardsToMove.isEmpty()) {
controller.moveCards(cardsToMove.getCards(game),
Zone.BATTLEFIELD, source, game, tapped, false, true, null);
return true;
}

View file

@ -53,6 +53,8 @@ public enum PlayerAction {
MANA_AUTO_PAYMENT_OFF,
MANA_AUTO_PAYMENT_RESTRICTED_ON,
MANA_AUTO_PAYMENT_RESTRICTED_OFF,
USE_FIRST_MANA_ABILITY_ON,
USE_FIRST_MANA_ABILITY_OFF,
RESET_AUTO_SELECT_REPLACEMENT_EFFECTS,
REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS,
REQUEST_PERMISSION_TO_SEE_HAND_CARDS,

View file

@ -0,0 +1,12 @@
package mage.constants;
/**
* Allows user to either tap a land for the first mode directly (shortcut)
* or have the normal method which pops up a menu
*
* @author spjspj
*/
public enum UseFirstManaAbilityMode {
NORMAL, FIRST
}

View file

@ -348,6 +348,8 @@ public interface Game extends MageItem, Serializable {
void setManaPaymentMode(UUID playerId, boolean autoPayment);
void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted);
void setUseFirstManaAbility(UUID playerId, boolean useFirstManaAbility);
void undo(UUID playerId);

View file

@ -1238,6 +1238,14 @@ public abstract class GameImpl implements Game, Serializable {
}
}
@Override
public synchronized void setUseFirstManaAbility(UUID playerId, boolean useFirstManaAbility) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.getUserData().setUseFirstManaAbility(useFirstManaAbility);
}
}
@Override
public void playPriority(UUID activePlayerId, boolean resuming) {
int errorContinueCounter = 0;

View file

@ -32,8 +32,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Abilities;
@ -59,6 +63,8 @@ import mage.game.combat.CombatGroup;
import mage.game.command.Command;
import mage.game.command.CommandObject;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent;
import mage.game.permanent.Battlefield;
import mage.game.permanent.Permanent;
import mage.game.stack.SpellStack;
@ -655,7 +661,9 @@ public class GameState implements Serializable, Copyable<GameState> {
if (!simultaneousEvents.isEmpty() && !getTurn().isEndTurnRequested()) {
// it can happen, that the events add new simultaneous events, so copy the list before
List<GameEvent> eventsToHandle = new ArrayList<>();
List<GameEvent> eventGroups = createEventGroups(simultaneousEvents, game);
eventsToHandle.addAll(simultaneousEvents);
eventsToHandle.addAll(eventGroups);
simultaneousEvents.clear();
for (GameEvent event : eventsToHandle) {
this.handleEvent(event, game);
@ -684,6 +692,76 @@ public class GameState implements Serializable, Copyable<GameState> {
return effects.replaceEvent(event, game);
}
public List<GameEvent> createEventGroups(List<GameEvent> events, Game game) {
class ZoneChangeData {
private final Zone fromZone;
private final Zone toZone;
private final UUID sourceId;
private final UUID playerId;
public ZoneChangeData(UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
this.sourceId = sourceId;
this.playerId = playerId;
this.fromZone = fromZone;
this.toZone = toZone;
}
@Override
public int hashCode() {
return (this.fromZone.ordinal() + 1) * 1
+ (this.toZone.ordinal() + 1) * 10
+ (this.sourceId != null ? this.sourceId.hashCode() : 0)
+ (this.playerId != null ? this.playerId.hashCode() : 0);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ZoneChangeData) {
ZoneChangeData data = (ZoneChangeData) obj;
return this.fromZone == data.fromZone
&& this.toZone == data.toZone
&& this.sourceId == data.sourceId
&& this.playerId == data.playerId;
}
return false;
}
}
Map<ZoneChangeData, List<GameEvent>> eventsByKey = new HashMap<>();
List<GameEvent> groupEvents = new LinkedList<>();
for (GameEvent event : events) {
if (event instanceof ZoneChangeEvent) {
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
ZoneChangeData key = new ZoneChangeData(castEvent.getSourceId(), castEvent.getPlayerId(), castEvent.getFromZone(), castEvent.getToZone());
if (eventsByKey.containsKey(key)) {
eventsByKey.get(key).add(event);
} else {
List<GameEvent> list = new LinkedList<>();
list.add(event);
eventsByKey.put(key, list);
}
}
}
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = new LinkedHashSet<>();
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
GameEvent event = it.next();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId();
Card card = game.getCard(targetId);
movedCards.add(card);
}
ZoneChangeData eventData = entry.getKey();
if (!movedCards.isEmpty()) {
ZoneChangeGroupEvent event = new ZoneChangeGroupEvent(movedCards, eventData.sourceId, eventData.playerId, eventData.fromZone, eventData.toZone);
groupEvents.add(event);
}
}
return groupEvents;
}
public void addCard(Card card) {
setZone(card.getId(), Zone.OUTSIDE);
for (Ability ability : card.getAbilities()) {

View file

@ -512,7 +512,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (canTransform) {
if (!replaceEvent(EventType.TRANSFORM, game)) {
setTransformed(!transformed);
fireEvent(EventType.TRANSFORMED, game);
game.applyEffects();
game.addSimultaneousEvent(GameEvent.getEvent(EventType.TRANSFORMED, getId(), getControllerId()));
return true;
}
}

View file

@ -123,7 +123,6 @@ import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent;
import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
@ -3249,9 +3248,6 @@ public abstract class PlayerImpl implements Player, Serializable {
default:
throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet");
}
if (!successfulMovedCards.isEmpty()) {
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
}
return successfulMovedCards.size() > 0;
}
@ -3267,7 +3263,6 @@ public abstract class PlayerImpl implements Player, Serializable {
if (cards.isEmpty()) {
return true;
}
game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), null, Zone.EXILED));
boolean result = false;
for (Card card : cards) {
Zone fromZone = game.getState().getZone(card.getId());
@ -3368,7 +3363,6 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
}
game.fireEvent(new ZoneChangeGroupEvent(movedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, Zone.GRAVEYARD));
return movedCards;
}

View file

@ -22,6 +22,7 @@ public class UserData implements Serializable {
protected boolean passPriorityCast;
protected boolean passPriorityActivation;
protected boolean autoOrderTrigger;
protected boolean useFirstManaAbility;
protected String matchHistory;
protected int matchQuitRatio;
@ -31,7 +32,7 @@ public class UserData implements Serializable {
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger) {
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility) {
this.groupId = userGroup.getGroupId();
this.avatarId = avatarId;
this.showAbilityPickerForced = showAbilityPickerForced;
@ -45,6 +46,7 @@ public class UserData implements Serializable {
this.passPriorityCast = passPriorityCast;
this.passPriorityActivation = passPriorityActivation;
this.autoOrderTrigger = autoOrderTrigger;
this.useFirstManaAbility = useFirstManaAbility;
this.matchHistory = "";
this.matchQuitRatio = 0;
this.tourneyHistory = "";
@ -65,10 +67,11 @@ public class UserData implements Serializable {
this.passPriorityCast = userData.passPriorityCast;
this.passPriorityActivation = userData.passPriorityActivation;
this.autoOrderTrigger = userData.autoOrderTrigger;
this.useFirstManaAbility = userData.useFirstManaAbility;
}
public static UserData getDefaultUserDataView() {
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false);
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false, false);
}
public void setGroupId(int groupId) {
@ -175,6 +178,14 @@ public class UserData implements Serializable {
this.autoOrderTrigger = autoOrderTrigger;
}
public boolean isUseFirstManaAbility() {
return useFirstManaAbility;
}
public void setUseFirstManaAbility(boolean useFirstManaAbility) {
this.useFirstManaAbility = useFirstManaAbility;
}
public String getHistory() {
if (UserGroup.COMPUTER.equals(this.groupId)) {
return "";

View file

@ -78,11 +78,11 @@ public class CardUtil {
// Enchantment subtypes
"Aura", "Curse", "Shrine",
// Artifact subtypes
"Equipment", "Fortification", "Contraption",
"Clue", "Equipment", "Fortification", "Contraption",
// Land subtypes
"Desert", "Gate", "Lair", "Locus", "Urza's", "Mine", "Power-Plant", "Tower",
// Planeswalker subtypes
"Ajani", "Ashiok", "Bolas", "Chandra", "Dack", "Daretti", "Domri", "Elspeth", "Freyalise", "Garruk", "Gideon", "Jace",
"Ajani", "Arlinn", "Ashiok", "Bolas", "Chandra", "Dack", "Daretti", "Domri", "Elspeth", "Freyalise", "Garruk", "Gideon", "Jace",
"Karn", "Kiora", "Koth", "Liliana", "Nahiri", "Nissa", "Narset", "Nixilis", "Ral", "Sarkhan", "Sorin", "Tamiyo", "Teferi",
"Tezzeret", "Tibalt", "Ugin", "Venser", "Vraska", "Xenagos",
// Instant sorcery subtypes

View file

@ -0,0 +1,99 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.watchers.common;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
/**
*
* @author LevelX2
*/
public class CastFromGraveyardWatcher extends Watcher {
// holds which spell with witch zone change counter was cast from graveyard
private final Map<UUID, HashSet<Integer>> spellsCastFromGraveyard = new HashMap<>();
public CastFromGraveyardWatcher() {
super(CastFromGraveyardWatcher.class.getName(), WatcherScope.GAME);
}
public CastFromGraveyardWatcher(final CastFromGraveyardWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
/**
* This does still not handle if a spell is cast from hand and comes to
* play from other zones during the same step. But at least the state is
* reset if the game comes to a new step
*/
if (event.getType() == GameEvent.EventType.SPELL_CAST && event.getZone().equals(Zone.GRAVEYARD)) {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null) {
HashSet<Integer> zcc = spellsCastFromGraveyard.get(spell.getSourceId());
if (zcc == null) {
zcc = new HashSet<>();
spellsCastFromGraveyard.put(spell.getSourceId(), zcc);
}
zcc.add(spell.getZoneChangeCounter(game));
}
}
}
public boolean spellWasCastFromGraveyard(UUID sourceId, int zcc) {
Set zccSet = spellsCastFromGraveyard.get(sourceId);
if (zccSet != null) {
return zccSet.contains(zcc);
}
return false;
}
@Override
public void reset() {
super.reset();
spellsCastFromGraveyard.clear();
}
@Override
public CastFromGraveyardWatcher copy() {
return new CastFromGraveyardWatcher(this);
}
}

View file

@ -0,0 +1,66 @@
package mage.watchers.common;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import static mage.game.events.GameEvent.EventType.CAST_SPELL;
import static mage.game.events.GameEvent.EventType.SPELL_CAST;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
/**
* @author jeffwadsworth
**/
public class FirstSpellCastThisTurnWatcher extends Watcher {
private final Map<UUID, UUID> playerFirstSpellCast = new HashMap<>();
private final Map<UUID, UUID> playerFirstCastSpell = new HashMap<>();
public FirstSpellCastThisTurnWatcher() {
super("FirstSpellCastThisTurn", WatcherScope.GAME);
}
public FirstSpellCastThisTurnWatcher(final FirstSpellCastThisTurnWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case SPELL_CAST:
case CAST_SPELL:
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null && !playerFirstSpellCast.containsKey(spell.getControllerId())) {
if (event.getType().equals(EventType.SPELL_CAST)) {
playerFirstSpellCast.put(spell.getControllerId(), spell.getId());
} else if (event.getType().equals(EventType.CAST_SPELL)) {
playerFirstCastSpell.put(spell.getControllerId(), spell.getId());
}
}
}
}
@Override
public FirstSpellCastThisTurnWatcher copy() {
return new FirstSpellCastThisTurnWatcher(this);
}
@Override
public void reset() {
super.reset();
playerFirstSpellCast.clear();
playerFirstCastSpell.clear();
}
public UUID getIdOfFirstCastSpell(UUID playerId) {
if (playerFirstSpellCast.get(playerId) == null) {
return playerFirstCastSpell.get(playerId);
} else {
return playerFirstSpellCast.get(playerId);
}
}
}

View file

@ -57051,7 +57051,7 @@ Behind the Scenes|Shadows over Innistrad|100|U|{2}{B}|Enchantment|||Creature you
Behold the Beyond|Shadows over Innistrad|101|M|{5}{B}{B}|Sorcery|||Discard your hand. Search your library for three cards and put those cards into your hand. Then shuffle your library.|
Biting Rain|Shadows over Innistrad|102|U|{2}{B}{B}|Sorcery|||All creatures get -2/-2 until end of turn.$Madness {2}{B} <i>(If you discard this card, discard it into exile. WHen you do, cast it for its madness cost or put it into your graveyard.)</i>|
Call the Bloodline|Shadows over Innistrad|103|U|{1}{B}|Enchantment|||{1}, Discard a card: Put a 1/1 black Vampire Knight token with lifelink onto the battlefield. Activate this ability only once each turn.|
Creeping Death|Shadows over Innistrad|104|U|{3}{B}|Enchantment|||At the beginning of your upkeep, each player discards a card. Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.|
Creeping Dread|Shadows over Innistrad|104|U|{3}{B}|Enchantment|||At the beginning of your upkeep, each player discards a card. Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.|
Crow of Dark Tidings|Shadows over Innistrad|105|C|{2}{B}|Creature - Zombie Bird|2|2|Flying$When Crow of Dark Tidings enters the battlefield or dies, put the top two cards of your library into your graveyard.|
Dead Weight|Shadows over Innistrad|106|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -2/-2.|
Diregraf Colossus|Shadows over Innistrad|107|R|{2}{B}|Creature - Zombie Giant|2|2|Diregraf Colossus enters the battlefield with a +1/+1 counter on it for each Zombie card in your graveyard.$Whenever you cast a Zombie spell, put a 2/2 black Zombie creature token onto the battlefield tapped.|
@ -57116,7 +57116,7 @@ Geier Reach Bandit|Shadows over Innistrad|159a|R|{2}{R}|Creature - Human Rogue W
Vildin-Pack Alpha|Shadows over Innistrad|159b|R||Creature - Werewolf|4|3|Whenever a Werewolf enters the battlefield under your control, you may transform it.$At the beginning of each upkeep, if a player cast two or more spells last turn, transform Vildin-Pack Alpha.|
Geistblast|Shadows over Innistrad|160|U|{2}{R}|Instant|||Geistblast deals 2 damage to target creature or player.${2}{U}, Exile Geist from your graveyard: Copy target instant or sorcery you control. You may choose new targets for the copy.|
Gibbering Fiend|Shadows over Innistrad|161|U|{1}{R}|Creature - Devil|2|1|When Gibbering Fiend enters the battlefield, it deals 1 damage to each opponent.$<i>Delirium</i> &mdash; At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, Gibbering Fiend deals 1 damage to that player.|
Goldknight Castigator|Shadows over Innistrad|162|M|{2}{R}{R}|Creature - Angel|4|9|Flying, haste$If a source would deal damage to you, it deals double that damage to you instead.$If a source would deal damage to Goldknight Castigator, it deals double that damage to Goldknight Castigator instead.|
Goldnight Castigator|Shadows over Innistrad|162|M|{2}{R}{R}|Creature - Angel|4|9|Flying, haste$If a source would deal damage to you, it deals double that damage to you instead.$If a source would deal damage to Goldnight Castigator, it deals double that damage to Goldnight Castigator instead.|
Harness the Storm|Shadows over Innistrad|163|R|{2}{R}|Enchantment|||Whenever you cast an instant or sorcery spell from your hand, you may cast target card with the same name as that spell from your graveyard.|
Howlpack Wolf|Shadows over Innistrad|164|C|{2}{R}|Creature - Wolf|3|3|Howlpack Wolf can't block unless you control another Wolf or Werewolf.|
Hulking Devil|Shadows over Innistrad|165|C|{3}{R}|Creature - Devil|5|2||