From 9df993bd3a08bac94af8847e12b6076e6ea2494a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 19 Aug 2015 00:59:05 +0200 Subject: [PATCH] * Intet, the Dreamer - Improved handling to look at face down cards exiled with Intet. Works now also if Intet has left the battlefield. --- .../src/mage/player/human/HumanPlayer.java | 30 ++-- .../mage/sets/gatecrash/BaneAlleyBroker.java | 106 ++++++----- .../mage/sets/newphyrexia/PraetorsGrasp.java | 9 +- .../sets/planarchaos/IntetTheDreamer.java | 146 ++++++++++----- .../java/org/mage/test/player/TestPlayer.java | 4 +- .../effects/common/DoIfCostPaid.java | 1 + .../abilities/keyword/HideawayAbility.java | 2 +- .../mage/constants/AsThoughEffectType.java | 2 +- Mage/src/mage/players/Player.java | 3 +- Mage/src/mage/players/PlayerImpl.java | 12 +- Mage/src/mage/util/CardUtil.java | 169 +++++++++--------- 11 files changed, 276 insertions(+), 208 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 2ed5be13a9f..c96384e4dcc 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -629,21 +629,23 @@ public class HumanPlayer extends PlayerImpl { if (object != null) { Zone zone = game.getState().getZone(object.getId()); if (zone != null) { - if (object instanceof Card && ((Card) object).isFaceDown(game)) { - revealFaceDownCard((Card) object, game); + if (object instanceof Card + && ((Card) object).isFaceDown(game) + && lookAtFaceDownCard((Card) object, game)) { result = true; - } - Player actingPlayer = null; - if (game.getPriorityPlayerId().equals(playerId)) { - actingPlayer = this; - } else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) { - actingPlayer = game.getPlayer(game.getPriorityPlayerId()); - } - if (actingPlayer != null) { - LinkedHashMap useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game); - if (useableAbilities != null && useableAbilities.size() > 0) { - activateAbility(useableAbilities, object, game); - result = true; + } else { + Player actingPlayer = null; + if (game.getPriorityPlayerId().equals(playerId)) { + actingPlayer = this; + } else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) { + actingPlayer = game.getPlayer(game.getPriorityPlayerId()); + } + if (actingPlayer != null) { + LinkedHashMap useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game); + if (useableAbilities != null && useableAbilities.size() > 0) { + activateAbility(useableAbilities, object, game); + result = true; + } } } } diff --git a/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java b/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java index 0443afca8af..864368f97ce 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java +++ b/Mage.Sets/src/mage/sets/gatecrash/BaneAlleyBroker.java @@ -30,12 +30,6 @@ package mage.sets.gatecrash; import java.util.HashSet; import java.util.Set; import java.util.UUID; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -48,8 +42,12 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.ExileZone; import mage.game.Game; @@ -62,29 +60,31 @@ import mage.util.CardUtil; /** * Gatecrash FAQ (01.2013) * - * If Bane Alley Broker's first ability resolves when you have no cards in your hand, - * you'll draw a card and then exile it. You won't have the opportunity to cast that - * card (or do anything else with it) before exiling it. + * If Bane Alley Broker's first ability resolves when you have no cards in your + * hand, you'll draw a card and then exile it. You won't have the opportunity to + * cast that card (or do anything else with it) before exiling it. * - * Due to a recent rules change, once you are allowed to look at a face-down card in - * exile, you are allowed to look at that card as long as it's exiled. If you no longer - * control Bane Alley Broker when its last ability resolves, you can continue to look - * at the relevant cards in exile to choose one to return. + * Due to a recent rules change, once you are allowed to look at a face-down + * card in exile, you are allowed to look at that card as long as it's exiled. + * If you no longer control Bane Alley Broker when its last ability resolves, + * you can continue to look at the relevant cards in exile to choose one to + * return. * - * Bane Alley Broker's second and third abilities apply to cards exiled with that - * specific Bane Alley Broker, not any other creature named Bane Alley Broker. - * You should keep cards exiled by different Bane Alley Brokers separate. + * Bane Alley Broker's second and third abilities apply to cards exiled with + * that specific Bane Alley Broker, not any other creature named Bane Alley + * Broker. You should keep cards exiled by different Bane Alley Brokers + * separate. * * If Bane Alley Broker leaves the battlefield, the cards exiled with it will be * exiled indefinitely. If it later returns to the battlefield, it will be a new - * object with no connection to the cards exiled with it in its previous existence. - * You won't be able to use the "new" Bane Alley Broker to return cards exiled with - * the "old" one. + * object with no connection to the cards exiled with it in its previous + * existence. You won't be able to use the "new" Bane Alley Broker to return + * cards exiled with the "old" one. * - * Even if not all players can look at the exiled cards, each card's owner is still - * known. It is advisable to keep cards owned by different players in distinct piles - * in case another player gains control of Bane Alley Broker and exiles one or more - * cards with it. + * Even if not all players can look at the exiled cards, each card's owner is + * still known. It is advisable to keep cards owned by different players in + * distinct piles in case another player gains control of Bane Alley Broker and + * exiles one or more cards with it. * * @author LevelX2 */ @@ -99,9 +99,9 @@ public class BaneAlleyBroker extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(3); - // {tap}: Draw a card, then exile a card from your hand face down. + // {tap}: Draw a card, then exile a card from your hand face down. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BaneAlleyBrokerDrawExileEffect(), new TapSourceCost())); - + // You may look at cards exiled with Bane Alley Broker. this.addAbility(new SimpleStaticAbility(Zone.ALL, new BaneAlleyBrokerLookAtCardEffect())); @@ -126,44 +126,44 @@ public class BaneAlleyBroker extends CardImpl { class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { public BaneAlleyBrokerDrawExileEffect() { - super(Outcome.DrawCard); - staticText = "Draw a card, then exile a card from your hand face down"; + super(Outcome.DrawCard); + staticText = "Draw a card, then exile a card from your hand face down"; } public BaneAlleyBrokerDrawExileEffect(final BaneAlleyBrokerDrawExileEffect effect) { - super(effect); + super(effect); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.drawCards(1, game); - Target target = new TargetCardInHand(new FilterCard("card to exile")); - if (controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (card != null && sourceObject != null) { - if (card.moveToExile(CardUtil.getCardExileZoneId(game, source), new StringBuilder(sourceObject.getName()).toString(), source.getSourceId(), game)) { - card.setFaceDown(true, game); - return true; - } - } - } - } - return false; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.drawCards(1, game); + Target target = new TargetCardInHand(new FilterCard("card to exile")); + if (controller.chooseTarget(outcome, target, source, game)) { + Card card = game.getCard(target.getFirstTarget()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (card != null && sourceObject != null) { + if (card.moveToExile(CardUtil.getCardExileZoneId(game, source), new StringBuilder(sourceObject.getName()).toString(), source.getSourceId(), game)) { + card.setFaceDown(true, game); + return true; + } + } + } + } + return false; } @Override public BaneAlleyBrokerDrawExileEffect copy() { - return new BaneAlleyBrokerDrawExileEffect(this); + return new BaneAlleyBrokerDrawExileEffect(this); } } class TargetCardInBaneAlleyBrokerExile extends TargetCard { public TargetCardInBaneAlleyBrokerExile(UUID CardId) { - super(1, 1, Zone.EXILED, new FilterCard("card exiled with Bane Alley Broker")); + super(1, 1, Zone.EXILED, new FilterCard("card exiled with Bane Alley Broker")); } public TargetCardInBaneAlleyBrokerExile(final TargetCardInBaneAlleyBrokerExile target) { @@ -223,7 +223,7 @@ class TargetCardInBaneAlleyBrokerExile extends TargetCard { class BaneAlleyBrokerLookAtCardEffect extends AsThoughEffectImpl { public BaneAlleyBrokerLookAtCardEffect() { - super(AsThoughEffectType.REVEAL_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); staticText = "You may look at cards exiled with {this}"; } @@ -252,13 +252,7 @@ class BaneAlleyBrokerLookAtCardEffect extends AsThoughEffectImpl { } UUID exileId = CardUtil.getCardExileZoneId(game, source); ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && exile.contains(objectId)) { - Cards cards = new CardsImpl(card); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.lookAtCards("Exiled with " + sourceObject.getName(), cards, game); - } - } + return exile != null && exile.contains(objectId); } } return false; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java index de9c500e10d..f101663e2b7 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java @@ -34,8 +34,6 @@ import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; @@ -167,7 +165,7 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { private final UUID cardId; public PraetorsGraspRevealEffect(UUID cardId) { - super(AsThoughEffectType.REVEAL_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); this.cardId = cardId; staticText = "You may look at and play that card for as long as it remains exiled"; } @@ -198,10 +196,7 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(cardId); if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { - if (controller.chooseUse(outcome, "Reveal exiled card?", source, game)) { - Cards cards = new CardsImpl(card); - controller.lookAtCards("Exiled with " + sourceObject.getIdName(), cards, game); - } + return true; } } else { discard(); diff --git a/Mage.Sets/src/mage/sets/planarchaos/IntetTheDreamer.java b/Mage.Sets/src/mage/sets/planarchaos/IntetTheDreamer.java index 88d32915c33..d3107a9bfb4 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/IntetTheDreamer.java +++ b/Mage.Sets/src/mage/sets/planarchaos/IntetTheDreamer.java @@ -27,21 +27,21 @@ */ package mage.sets.planarchaos; +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.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; @@ -59,6 +59,8 @@ import mage.util.CardUtil; */ public class IntetTheDreamer extends CardImpl { + protected static final String VALUE_PREFIX = "ExileZones"; + public IntetTheDreamer(UUID ownerId) { super(ownerId, 158, "Intet, the Dreamer", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}{R}{G}"); this.expansionSetCode = "PLC"; @@ -72,9 +74,12 @@ public class IntetTheDreamer extends CardImpl { // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl("{2}{U}")), false, true)); - // You may look at that card for as long as it remains exiled. You may play that card without paying its mana cost for as long as Intet remains on the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerEffect())); + // You may look at that card for as long as it remains exiled. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect())); + // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerCastEffect())); } + public IntetTheDreamer(final IntetTheDreamer card) { super(card); } @@ -86,46 +91,53 @@ public class IntetTheDreamer extends CardImpl { } class IntetTheDreamerExileEffect extends OneShotEffect { - + public IntetTheDreamerExileEffect() { super(Outcome.Discard); staticText = "exile the top card of your library face down"; } - + public IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) { super(effect); } - + @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card card = player.getLibrary().getFromTop(game); - MageObject sourceObject = source.getSourceObject(game); - if (card != null && sourceObject != null) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - player.moveCardToExileWithInfo(card, exileZoneId, sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, false); - card.setFaceDown(true, game); - return true; - } - } - return false; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = controller.getLibrary().getFromTop(game); + MageObject sourceObject = source.getSourceObject(game); + if (card != null && sourceObject != null) { + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + card.setFaceDown(true, game); + controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); + card.setFaceDown(true, game); + Set exileZones = (Set) game.getState().getValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString()); + if (exileZones == null) { + exileZones = new HashSet<>(); + game.getState().setValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString(), exileZones); + } + exileZones.add(exileZoneId); + return true; + } + } + return false; } - + @Override public IntetTheDreamerExileEffect copy() { return new IntetTheDreamerExileEffect(this); } } -class IntetTheDreamerEffect extends AsThoughEffectImpl { +class IntetTheDreamerCastEffect extends AsThoughEffectImpl { - public IntetTheDreamerEffect() { + public IntetTheDreamerCastEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "You may play the card from exile without paying its mana cost for as long as {this} remains on the battlefield"; } - public IntetTheDreamerEffect(final IntetTheDreamerEffect effect) { + public IntetTheDreamerCastEffect(final IntetTheDreamerCastEffect effect) { super(effect); } @@ -135,30 +147,80 @@ class IntetTheDreamerEffect extends AsThoughEffectImpl { } @Override - public IntetTheDreamerEffect copy() { - return new IntetTheDreamerEffect(this); + public IntetTheDreamerCastEffect copy() { + return new IntetTheDreamerCastEffect(this); } @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - Card card = game.getCard(objectId); - if (affectedControllerId.equals(source.getControllerId()) && card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); - if (zone != null && zone.contains(card.getId())) { - if (controller.chooseUse(outcome, "Look at the card?", source, game)) { - Cards cards = new CardsImpl(); - cards.add(card); - controller.lookAtCards(sourceObject.getName(), cards, game); - return false; - } - controller.setCastSourceIdWithAlternateMana(objectId, null); - return true; + if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId).equals(Zone.EXILED)) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + Card card = game.getCard(objectId); + if (card != null && card.isFaceDown(game)) { + ExileZone zone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + if (zone != null && zone.contains(card.getId())/* && CardUtil.cardCanBePlayedNow(card, controller.getId(), game)*/) { + if (card.getCardType().contains(CardType.LAND)) { + if (game.canPlaySorcery(controller.getId()) && game.getPlayer(controller.getId()).canPlayLand()) { + return controller.chooseUse(outcome, "Play " + card.getName() + "?", source, game); + } + } else { + controller.setCastSourceIdWithAlternateMana(objectId, null); + return true; + } + } + } + } + } + return false; + + } +} + +class IntetTheDreamerLookEffect extends AsThoughEffectImpl { + + public IntetTheDreamerLookEffect() { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + staticText = "You may look at that card for as long as it remains exiled"; + } + + public IntetTheDreamerLookEffect(final IntetTheDreamerLookEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public IntetTheDreamerLookEffect copy() { + return new IntetTheDreamerLookEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId).equals(Zone.EXILED)) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + Card card = game.getCard(objectId); + if (card != null && card.isFaceDown(game)) { + Set exileZones = (Set) game.getState().getValue(IntetTheDreamer.VALUE_PREFIX + source.getSourceId().toString()); + if (exileZones != null) { + for (ExileZone exileZone : game.getExile().getExileZones()) { + if (exileZone.contains(objectId)) { + if (!exileZones.contains(exileZone.getId())) { + return false; + } + } + } + return true; + } } } } return false; } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 100ebafb3e1..77263561b37 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1613,8 +1613,8 @@ public class TestPlayer implements Player { } @Override - public void revealFaceDownCard(Card card, Game game) { - computerPlayer.revealFaceDownCard(card, game); + public boolean lookAtFaceDownCard(Card card, Game game) { + return computerPlayer.lookAtFaceDownCard(card, game); } @Override diff --git a/Mage/src/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/mage/abilities/effects/common/DoIfCostPaid.java index 000ee2e2753..35ff9555c63 100644 --- a/Mage/src/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/mage/abilities/effects/common/DoIfCostPaid.java @@ -53,6 +53,7 @@ public class DoIfCostPaid extends OneShotEffect { effectText = effectText.substring(0, effectText.length() - 1); } message = getCostText() + " and " + effectText + "?"; + message = Character.toUpperCase(message.charAt(0)) + message.substring(1); } else { message = chooseUseText; } diff --git a/Mage/src/mage/abilities/keyword/HideawayAbility.java b/Mage/src/mage/abilities/keyword/HideawayAbility.java index 124e7eec056..57ff119d397 100644 --- a/Mage/src/mage/abilities/keyword/HideawayAbility.java +++ b/Mage/src/mage/abilities/keyword/HideawayAbility.java @@ -139,7 +139,7 @@ class HideawayExileEffect extends OneShotEffect { class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl { public HideawayLookAtFaceDownCardEffect() { - super(AsThoughEffectType.REVEAL_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); staticText = "You may look at cards exiled with {this}"; } diff --git a/Mage/src/mage/constants/AsThoughEffectType.java b/Mage/src/mage/constants/AsThoughEffectType.java index 433fbd7c038..61c98018f24 100644 --- a/Mage/src/mage/constants/AsThoughEffectType.java +++ b/Mage/src/mage/constants/AsThoughEffectType.java @@ -17,7 +17,7 @@ public enum AsThoughEffectType { DAMAGE, HEXPROOF, PAY, - REVEAL_FACE_DOWN, + LOOK_AT_FACE_DOWN, SPEND_ANY_MANA, TARGET } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 80c17cce2c8..ffb4dd09615 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -566,8 +566,9 @@ public interface Player extends MageItem, Copyable { * * @param card * @param game + * @return player looked at the card */ - void revealFaceDownCard(Card card, Game game); + boolean lookAtFaceDownCard(Card card, Game game); /** * Set seconds left to play the game. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 8eb7b8bed84..ac3222ee895 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2830,11 +2830,15 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void revealFaceDownCard(Card card, Game game) { - if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.REVEAL_FACE_DOWN, this.getId(), game)) { - Cards cards = new CardsImpl(card); - this.revealCards(getName(), cards, game); + public boolean lookAtFaceDownCard(Card card, Game game) { + if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, this.getId(), game)) { + if (chooseUse(Outcome.Benefit, "Look at that card?", null, game)) { + Cards cards = new CardsImpl(card); + this.lookAtCards(getName(), cards, game); + return true; + } } + return false; } @Override diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index 475c3a2c107..3af5676fa48 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.util; import java.util.Arrays; @@ -58,7 +57,6 @@ import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.util.functions.CopyTokenFunction; - /** * @author nantuko */ @@ -71,16 +69,16 @@ public class CardUtil { private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*"; private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone"; - - static String numberStrings[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "ninteen", "twenty"}; - public static final String[] NON_CHANGELING_SUBTYPES_VALUES = new String[] { "Mountain", "Forest", "Plains", "Swamp", "Island", - "Aura", "Curse", "Shrine", - "Equipment", "Fortification", "Contraption", - "Trap", "Arcane"}; - public static final Set NON_CREATURE_SUBTYPES = new HashSet<>(Arrays.asList(NON_CHANGELING_SUBTYPES_VALUES)); - + static String numberStrings[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", + "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "ninteen", "twenty"}; + + public static final String[] NON_CHANGELING_SUBTYPES_VALUES = new String[]{"Mountain", "Forest", "Plains", "Swamp", "Island", + "Aura", "Curse", "Shrine", + "Equipment", "Fortification", "Contraption", + "Trap", "Arcane"}; + public static final Set NON_CREATURE_SUBTYPES = new HashSet<>(Arrays.asList(NON_CHANGELING_SUBTYPES_VALUES)); + /** * Checks whether two cards share card types. * @@ -102,6 +100,7 @@ public class CardUtil { return false; } + /** * Checks whether two cards share card subtypes. * @@ -116,10 +115,10 @@ public class CardUtil { } if (card1.getCardType().contains(CardType.CREATURE) && card2.getCardType().contains(CardType.CREATURE)) { - if (card1.getAbilities().contains(ChangelingAbility.getInstance()) || - card1.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE) || - card2.getAbilities().contains(ChangelingAbility.getInstance()) || - card2.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE)) { + if (card1.getAbilities().contains(ChangelingAbility.getInstance()) + || card1.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE) + || card2.getAbilities().contains(ChangelingAbility.getInstance()) + || card2.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE)) { return true; } } @@ -164,7 +163,7 @@ public class CardUtil { CardUtil.adjustAbilityCost((Ability) spellAbility, reduceCount); adjustAlternativeCosts(spellAbility, reduceCount); } - + public static ManaCosts increaseCost(ManaCosts manaCosts, int increaseCount) { return adjustCost(manaCosts, -increaseCount); } @@ -207,8 +206,6 @@ public class CardUtil { } } - - /** * Adjusts ability cost to be paid. * @@ -220,10 +217,10 @@ public class CardUtil { ability.getManaCostsToPay().clear(); ability.getManaCostsToPay().addAll(adjustedCost); } - + private static ManaCosts adjustCost(ManaCosts manaCosts, int reduceCount) { int restToReduce = reduceCount; - ManaCosts adjustedCost = new ManaCostsImpl<>(); + ManaCosts adjustedCost = new ManaCostsImpl<>(); boolean updated = false; for (ManaCost manaCost : manaCosts) { Mana mana = manaCost.getOptions().get(0); @@ -251,18 +248,18 @@ public class CardUtil { public static ManaCosts removeVariableManaCost(ManaCosts manaCosts) { ManaCosts adjustedCost = new ManaCostsImpl<>(); - for (ManaCost manaCost: manaCosts) { + for (ManaCost manaCost : manaCosts) { if (!(manaCost instanceof VariableManaCost)) { adjustedCost.add(manaCost); } } return adjustedCost; } - + public static void reduceCost(SpellAbility spellAbility, ManaCosts manaCostsToReduce) { adjustCost(spellAbility, manaCostsToReduce, true); } - + public static void increaseCost(SpellAbility spellAbility, ManaCosts manaCostsToIncrease) { ManaCosts increasedCost = spellAbility.getManaCostsToPay().copy(); @@ -279,19 +276,19 @@ public class CardUtil { * * @param spellAbility * @param manaCostsToReduce costs to reduce - * @param convertToGeneric colored mana does reduce generic mana if no appropriate colored mana is in the costs included + * @param convertToGeneric colored mana does reduce generic mana if no + * appropriate colored mana is in the costs included */ public static void adjustCost(SpellAbility spellAbility, ManaCosts manaCostsToReduce, boolean convertToGeneric) { ManaCosts previousCost = spellAbility.getManaCostsToPay(); - ManaCosts adjustedCost = new ManaCostsImpl<>(); + ManaCosts adjustedCost = new ManaCostsImpl<>(); // save X value (e.g. convoke ability) - for (VariableCost vCost: previousCost.getVariableCosts()) { + for (VariableCost vCost : previousCost.getVariableCosts()) { if (vCost instanceof VariableManaCost) { adjustedCost.add((VariableManaCost) vCost); } } - Mana reduceMana = new Mana(); for (ManaCost manaCost : manaCostsToReduce) { reduceMana.add(manaCost.getMana()); @@ -304,46 +301,46 @@ public class CardUtil { } if (mana.getBlack() > 0 && reduceMana.getBlack() > 0) { if (reduceMana.getBlack() > mana.getBlack()) { - reduceMana.setBlack(reduceMana.getBlack()-mana.getBlack()); + reduceMana.setBlack(reduceMana.getBlack() - mana.getBlack()); mana.setBlack(0); } else { - mana.setBlack(mana.getBlack()-reduceMana.getBlack()); + mana.setBlack(mana.getBlack() - reduceMana.getBlack()); reduceMana.setBlack(0); } } if (mana.getRed() > 0 && reduceMana.getRed() > 0) { if (reduceMana.getRed() > mana.getRed()) { - reduceMana.setRed(reduceMana.getRed()-mana.getRed()); + reduceMana.setRed(reduceMana.getRed() - mana.getRed()); mana.setRed(0); } else { - mana.setRed(mana.getRed()-reduceMana.getRed()); + mana.setRed(mana.getRed() - reduceMana.getRed()); reduceMana.setRed(0); } } if (mana.getBlue() > 0 && reduceMana.getBlue() > 0) { if (reduceMana.getBlue() > mana.getBlue()) { - reduceMana.setBlue(reduceMana.getBlue()-mana.getBlue()); + reduceMana.setBlue(reduceMana.getBlue() - mana.getBlue()); mana.setBlue(0); } else { - mana.setBlue(mana.getBlue()-reduceMana.getBlue()); + mana.setBlue(mana.getBlue() - reduceMana.getBlue()); reduceMana.setBlue(0); } } if (mana.getGreen() > 0 && reduceMana.getGreen() > 0) { if (reduceMana.getGreen() > mana.getGreen()) { - reduceMana.setGreen(reduceMana.getGreen()-mana.getGreen()); + reduceMana.setGreen(reduceMana.getGreen() - mana.getGreen()); mana.setGreen(0); } else { - mana.setGreen(mana.getGreen()-reduceMana.getGreen()); + mana.setGreen(mana.getGreen() - reduceMana.getGreen()); reduceMana.setGreen(0); } } if (mana.getWhite() > 0 && reduceMana.getWhite() > 0) { if (reduceMana.getWhite() > mana.getWhite()) { - reduceMana.setWhite(reduceMana.getWhite()-mana.getWhite()); + reduceMana.setWhite(reduceMana.getWhite() - mana.getWhite()); mana.setWhite(0); } else { - mana.setWhite(mana.getWhite()-reduceMana.getWhite()); + mana.setWhite(mana.getWhite() - reduceMana.getWhite()); reduceMana.setWhite(0); } } @@ -384,10 +381,10 @@ public class CardUtil { spellAbility.getManaCostsToPay().clear(); spellAbility.getManaCostsToPay().addAll(adjustedCost); } - /** - * Returns function that copies params\abilities from one card to {@link Token}. + * Returns function that copies params\abilities from one card to + * {@link Token}. * * @param target * @return @@ -396,7 +393,7 @@ public class CardUtil { return new CopyTokenFunction(target); } - public static boolean isPermanentCard ( Card card ) { + public static boolean isPermanentCard(Card card) { boolean permanent = false; permanent |= card.getCardType().contains(CardType.ARTIFACT); @@ -409,23 +406,24 @@ public class CardUtil { } /** - * Converts an integer number to string - * Numbers > 20 will be returned as digits + * Converts an integer number to string Numbers > 20 will be returned as + * digits * * @param number - * @return + * @return */ public static String numberToText(int number) { return numberToText(number, "one"); } - + /** * Converts an integer number to string like "one", "two", "three", ... * Numbers > 20 will be returned as digits - * + * * @param number number to convert to text - * @param forOne if the number is 1, this string will be returnedinstead of "one". - * @return + * @param forOne if the number is 1, this string will be returnedinstead of + * "one". + * @return */ public static String numberToText(int number, String forOne) { if (number == 1 && forOne != null) { @@ -458,8 +456,8 @@ public class CardUtil { } public static boolean checkNumeric(String s) { - for(int i = 0; i < s.length(); i++) { - if(!Character.isDigit(s.charAt(i))) { + for (int i = 0; i < s.length(); i++) { + if (!Character.isDigit(s.charAt(i))) { return false; } } @@ -476,7 +474,7 @@ public class CardUtil { public static UUID getCardExileZoneId(Game game, Ability source) { return getCardExileZoneId(game, source.getSourceId()); } - + public static UUID getCardExileZoneId(Game game, UUID sourceId) { return getCardExileZoneId(game, sourceId, false); } @@ -484,7 +482,7 @@ public class CardUtil { public static UUID getCardExileZoneId(Game game, UUID sourceId, boolean previous) { return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game); } - + public static UUID getObjectExileZoneId(Game game, MageObject mageObject) { return getObjectExileZoneId(game, mageObject, false); } @@ -499,26 +497,27 @@ public class CardUtil { if (zoneChangeCounter > 0 && previous) { zoneChangeCounter--; } - return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT,mageObject.getId(), game, zoneChangeCounter, false), game); + return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, mageObject.getId(), game, zoneChangeCounter, false), game); } - + public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) { - return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT,objectId, game, zoneChangeCounter, false), game); + return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game); } - + public static UUID getExileZoneId(String key, Game game) { UUID exileId = (UUID) game.getState().getValue(key); if (exileId == null) { exileId = UUID.randomUUID(); game.getState().setValue(key, exileId); } - return exileId; + return exileId; } /** - * Creates a string from text + cardId and the zoneChangeCounter of the card (from cardId). - * This string can be used to save and get values that must be specific to a permanent instance. - * So they won't match, if a permanent was e.g. exiled and came back immediately. + * Creates a string from text + cardId and the zoneChangeCounter of the card + * (from cardId). This string can be used to save and get values that must + * be specific to a permanent instance. So they won't match, if a permanent + * was e.g. exiled and came back immediately. * * @param text short value to describe the value * @param cardId id of the card @@ -529,15 +528,15 @@ public class CardUtil { return getCardZoneString(text, cardId, game, false); } - public static String getCardZoneString(String text, UUID cardId, Game game, boolean previous) { - int zoneChangeCounter= 0; + public static String getCardZoneString(String text, UUID cardId, Game game, boolean previous) { + int zoneChangeCounter = 0; Card card = game.getCard(cardId); // if called for a token, the id is enough if (card != null) { zoneChangeCounter = card.getZoneChangeCounter(game); } - return getObjectZoneString(text,cardId, game, zoneChangeCounter, previous); + return getObjectZoneString(text, cardId, game, zoneChangeCounter, previous); } - + public static String getObjectZoneString(String text, MageObject mageObject, Game game) { int zoneChangeCounter = 0; if (mageObject instanceof Permanent) { @@ -547,22 +546,23 @@ public class CardUtil { } return getObjectZoneString(text, mageObject.getId(), game, zoneChangeCounter, false); } - + public static String getObjectZoneString(String text, UUID objectId, Game game, int zoneChangeCounter, boolean previous) { StringBuilder uniqueString = new StringBuilder(); if (text != null) { uniqueString.append(text); } uniqueString.append(objectId); - uniqueString.append(previous ? zoneChangeCounter - 1: zoneChangeCounter); - return uniqueString.toString(); + uniqueString.append(previous ? zoneChangeCounter - 1 : zoneChangeCounter); + return uniqueString.toString(); } - + /** - * Returns if the ability is used to check which cards - * are playable on hand. (Issue #457) + * Returns if the ability is used to check which cards are playable on hand. + * (Issue #457) + * * @param ability - ability to check - * @return + * @return */ public static boolean isCheckPlayableMode(Ability ability) { if (ability instanceof ActivatedAbility) { @@ -572,8 +572,8 @@ public class CardUtil { } /** - * Adds tags to mark the additional info of a card - * (e.g. blue font color) + * Adds tags to mark the additional info of a card (e.g. blue font color) + * * @param text text body * @return */ @@ -584,7 +584,7 @@ public class CardUtil { public static boolean convertedManaCostsIsEqual(MageObject object1, MageObject object2) { Set cmcObject1 = getCMC(object1); Set cmcObject2 = getCMC(object2); - for (Integer integer :cmcObject1) { + for (Integer integer : cmcObject1) { if (cmcObject2.contains(integer)) { return true; } @@ -595,7 +595,7 @@ public class CardUtil { public static Set getCMC(MageObject object) { Set cmcObject = new HashSet<>(); if (object instanceof Spell) { - cmcObject.add(((Spell)object).getConvertedManaCost()); + cmcObject.add(((Spell) object).getConvertedManaCost()); } else if (object instanceof Card) { Card card = (Card) object; if (card instanceof SplitCard) { @@ -605,16 +605,16 @@ public class CardUtil { } else { cmcObject.add(card.getManaCost().convertedManaCost()); } - } + } return cmcObject; } - + /** - * Gets the colors that are in the casting cost but also in the rules text + * Gets the colors that are in the casting cost but also in the rules text * as far as not included in reminder text. - * + * * @param card - * @return + * @return */ public static FilterMana getColorIdentity(Card card) { FilterMana mana = new FilterMana(); @@ -644,8 +644,17 @@ public class CardUtil { } return mana; } - + public static boolean isNonCreatureSubtype(String subtype) { return NON_CREATURE_SUBTYPES.contains(subtype); } + + public static boolean cardCanBePlayedNow(Card card, UUID playerId, Game game) { + if (card.getCardType().contains(CardType.LAND)) { + return game.canPlaySorcery(playerId) && game.getPlayer(playerId).canPlayLand(); + } else { + return card.getSpellAbility() != null && card.getSpellAbility().spellCanBeActivatedRegularlyNow(playerId, game); + } + } + }