diff --git a/res/cardsfolder/c/chains_of_mephistopheles.txt b/res/cardsfolder/c/chains_of_mephistopheles.txt index f39312269d1..23dfe643e39 100644 --- a/res/cardsfolder/c/chains_of_mephistopheles.txt +++ b/res/cardsfolder/c/chains_of_mephistopheles.txt @@ -2,6 +2,8 @@ Name:Chains of Mephistopheles ManaCost:1 B Types:Enchantment Text:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard. +SVar:MillOne:DB$ Mill | NumCards$ 1 +SVar:DiscardOne:DB$ Discard | Mandatory$ True | NumCards$ 1 | Mode$ TgtChoose SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/chains_of_mephistopheles.jpg Oracle:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard. diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index a25749a8a34..ba81d6d9644 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -9052,7 +9052,7 @@ public class Card extends GameEntity implements Comparable { * the rE */ public void addReplacementEffect(final ReplacementEffect replacementEffect) { - final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy(); + final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy(); // doubtful - every caller provides a newly parsed instance, why copy? replacementEffectCopy.setHostCard(this); this.getCharacteristics().getReplacementEffects().add(replacementEffectCopy); } diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 2c9fb6545de..9893b613a98 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -265,7 +265,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) { this.setPlayersPriorityPermission(false); } else { - this.getPlayerTurn().drawCards(1, true); + this.getPlayerTurn().drawCard(); } break; diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index 41271b4b875..fa3ede2880a 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -144,17 +144,6 @@ public class AIPlayer extends Player { } } - /* - * (non-Javadoc) - * - * @see forge.Player#discard_Chains_of_Mephistopheles() - */ - @Override - protected final void discardChainsOfMephistopheles() { - this.discard(1, null); - this.drawCard(); - } - /* (non-Javadoc) * @see forge.game.player.Player#getType() */ diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index 49633907fd5..a752e6ceebb 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -100,19 +100,6 @@ public class HumanPlayer extends Player { } } - /* - * (non-Javadoc) - * - * @see forge.Player#discard_Chains_of_Mephistopheles() - */ - /** - * - */ - @Override - protected final void discardChainsOfMephistopheles() { - Singletons.getModel().getMatch().getInput().setInputInterrupt(PlayerUtil.inputChainsDiscard()); - } - /** {@inheritDoc} */ @Override public final void sacrificePermanent(final String prompt, final List choices) { diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 695393e853e..9ee074e8d5d 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -42,6 +42,8 @@ import forge.Constant.Preferences; import forge.CounterType; import forge.GameEntity; import forge.Singletons; +import forge.card.ability.AbilityFactory; +import forge.card.ability.AbilityUtils; import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.mana.ManaPool; @@ -50,6 +52,7 @@ import forge.card.replacement.ReplacementResult; import forge.card.spellability.Ability; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; +import forge.card.spellability.Target; import forge.card.staticability.StaticAbility; import forge.card.trigger.TriggerType; import forge.game.GameActionUtil; @@ -65,6 +68,7 @@ import forge.game.event.MulliganEvent; import forge.game.event.PoisonCounterEvent; import forge.game.event.ShuffleEvent; import forge.game.phase.PhaseHandler; +import forge.game.phase.PhaseType; import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.Zone; @@ -125,6 +129,7 @@ public abstract class Player extends GameEntity implements Comparable { /** The num drawn this turn. */ private int numDrawnThisTurn = 0; + private int numDrawnThisDrawStep = 0; /** The slowtrip list. */ private List slowtripList = new ArrayList(); @@ -1239,70 +1244,6 @@ public abstract class Player extends GameEntity implements Comparable { */ public abstract boolean dredge(); - /** - *

- * drawCards. - *

- * - * @param n - * a int. - * @return a List of cards actually drawn - */ - public final List drawCards(final int n) { - return this.drawCards(n, false); - } - - /** - *

- * drawCards. - *

- * - * @param n - * a int. - * @param firstFromDraw - * true if this is the card drawn from that player's draw step - * each turn - * @return a List of cards actually drawn - */ - public final List drawCards(final int n, final boolean firstFromDraw) { - final List drawn = new ArrayList(); - - if (!this.canDraw()) { - return drawn; - } - - for (int i = 0; i < n; i++) { - - // TODO: multiple replacements need to be selected by the controller - if (this.getDredge().size() != 0) { - if (this.dredge()) { - continue; - } - } - - if (!firstFromDraw && game.isCardInPlay("Chains of Mephistopheles")) { - if (!this.getZone(ZoneType.Hand).isEmpty()) { - if (this.isHuman()) { - this.discardChainsOfMephistopheles(); - } else { // Computer - this.discard(1, null); - // true causes this code not to be run again - drawn.addAll(this.drawCards(1, true)); - } - } else { - this.mill(1); - } - } else { - drawn.addAll(this.doDraw()); - } - } - - // Play the Draw sound - game.getEvents().post(new DrawCardEvent()); - - return drawn; - } - /** * * TODO Write javadoc for this method. @@ -1328,6 +1269,40 @@ public abstract class Player extends GameEntity implements Comparable { stats.notifyOpeningHandSize(newHand); } + /** + *

+ * drawCards. + *

+ * + * @param n + * a int. + * @return a List of cards actually drawn + */ + public final List drawCards(final int n) { + final List drawn = new ArrayList(); + + if (!this.canDraw()) { + return drawn; + } + + for (int i = 0; i < n; i++) { + + // TODO: multiple replacements need to be selected by the controller + if (!this.getDredge().isEmpty()) { + if (this.dredge()) { + continue; + } + } + + drawn.addAll(this.doDraw()); + } + + // Play the Draw sound + game.getEvents().post(new DrawCardEvent()); + + return drawn; + } + /** *

* doDraw. @@ -1348,7 +1323,46 @@ public abstract class Player extends GameEntity implements Comparable { return drawn; } - if (library.size() != 0) { + // ======== Chains of Mephistopheles hardcode. ========= + // This card requires player to either discard a card, and then he may proceed drawing, or mill 1 - then no draw will happen + // It's oracle-worded as a replacement effect ("If a player would draw a card ... discards a card instead") (rule 419.1a) + // Yet, gatherer's rulings read: The effect is cumulative. If there are two of these on the battlefield, each of them will modify each draw + // That means player isn't supposed to choose one replacement effect out of two (generated by Chains Of Mephistopheles), but both happen. + // So it's not a common replacement effect and has to handled by special code. + + // This is why the code is placed after any other replacement effects could affect the draw event. + List chainsList = null; + for(Card c: game.getCardsInGame()) { + if ( c.getName().equals("Chains of Mephistopheles") ) { + if ( null == chainsList ) + chainsList = new ArrayList(); + chainsList.add(c); + } + } + if (chainsList != null && (numDrawnThisDrawStep > 0 || !game.getPhaseHandler().is(PhaseType.DRAW))) { + for(Card c : chainsList) { + // I have to target this player - don't know how to do it. + Target target = new Target(c, null, ""); + target.addTarget(this); + + if (getCardsIn(ZoneType.Hand).isEmpty()) { + SpellAbility saMill = AbilityFactory.getAbility(c.getSVar("MillOne"), c); + saMill.setActivatingPlayer(c.getController()); + saMill.setTarget(target); + AbilityUtils.resolve(saMill, false); + + return drawn; // Draw is cancelled + } else { + SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c); + saDiscard.setActivatingPlayer(c.getController()); + saDiscard.setTarget(target); + AbilityUtils.resolve(saDiscard, false); + } + } + } + // End of = Chains of Mephistopheles hardcode. ========= + + if (!library.isEmpty()) { Card c = library.get(0); c = game.getAction().moveToHand(c); @@ -1371,6 +1385,8 @@ public abstract class Player extends GameEntity implements Comparable { this.setLastDrawnCard(c); c.setDrawnThisTurn(true); this.numDrawnThisTurn++; + if ( game.getPhaseHandler().is(PhaseType.DRAW)) + this.numDrawnThisDrawStep++; // Miracle draws if (this.numDrawnThisTurn == 1 && game.getPhaseHandler().getTurn() != 0) { @@ -1550,6 +1566,7 @@ public abstract class Player extends GameEntity implements Comparable { */ public final void resetNumDrawnThisTurn() { this.numDrawnThisTurn = 0; + this.numDrawnThisDrawStep = 0; } /** @@ -1569,11 +1586,6 @@ public abstract class Player extends GameEntity implements Comparable { // / // ////////////////////////////// - /** - * Discard_ chains_of_ mephistopheles. - */ - protected abstract void discardChainsOfMephistopheles(); - /** *

* discard. diff --git a/src/main/java/forge/game/player/PlayerUtil.java b/src/main/java/forge/game/player/PlayerUtil.java index 743822cc362..39d852e5785 100644 --- a/src/main/java/forge/game/player/PlayerUtil.java +++ b/src/main/java/forge/game/player/PlayerUtil.java @@ -154,45 +154,6 @@ public final class PlayerUtil { return target; } // input_discard() - /** - *

- * input_chainsDiscard. - *

- * - * @return a {@link forge.control.input.Input} object. - */ - public static Input inputChainsDiscard() { - final Input target = new Input() { - private static final long serialVersionUID = 2856894846224546303L; - - @Override - public void showMessage() { - if (Singletons.getControl().getPlayer().getZone(ZoneType.Hand).size() == 0) { - this.stop(); - } - - CMatchUI.SINGLETON_INSTANCE.showMessage("Chains of Mephistopheles:\n" + "Select a card to discard"); - ButtonUtil.disableAll(); - } - - @Override - public void selectCard(final Card card) { - Zone zone = Singletons.getModel().getGame().getZoneOf(card); - if (zone.is(ZoneType.Hand)) { - card.getController().discard(card, null); - this.done(); - } - } - - private void done() { - this.stop(); - // hack to not trigger Chains of Mephistopheles recursively - Singletons.getControl().getPlayer().drawCards(1, true); - } - }; - return target; - } // input_chainsDiscard() - /** *

* input_sacrificePermanent.