From d244551e3b3275746635ab4f294c9597dd377765 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 9 Jun 2014 17:53:55 +0200 Subject: [PATCH] * Added Morph ability. --- .../mage/client/util/gui/GuiDisplayUtil.java | 7 +- .../java/org/mage/card/arcane/CardPanel.java | 11 +- .../mage/plugins/card/images/ImageCache.java | 32 +- .../plugins/card/utils/CardImageUtils.java | 3 +- Mage.Common/src/mage/view/AbilityView.java | 14 +- Mage.Common/src/mage/view/CardView.java | 88 ++++- .../src/mage/view/CombatGroupView.java | 19 +- Mage.Common/src/mage/view/GameEndView.java | 2 +- Mage.Common/src/mage/view/GameView.java | 18 +- Mage.Common/src/mage/view/PermanentView.java | 64 +++- Mage.Common/src/mage/view/PlayerView.java | 4 +- .../java/mage/server/game/GameController.java | 2 +- .../java/mage/server/game/GameSession.java | 2 +- .../java/mage/server/game/GameWatcher.java | 2 +- .../java/mage/server/game/ReplaySession.java | 4 +- .../newphyrexia/VorinclexVoiceOfHunger.java | 76 ++-- Mage/src/mage/MageObject.java | 1 + Mage/src/mage/MageObjectImpl.java | 4 + Mage/src/mage/abilities/AbilityImpl.java | 4 +- .../mage/abilities/TriggeredAbilityImpl.java | 6 +- .../abilities/common/TurnFaceUpAbility.java | 97 +++++ .../common/TurnedFaceUpTriggeredAbility.java | 45 +++ .../costs/common/RemoveCounterCost.java | 4 +- .../abilities/effects/ContinuousEffects.java | 2 +- .../effects/EntersBattlefieldEffect.java | 2 +- .../PlaneswalkerRedirectionEffect.java | 2 +- .../effects/common/ChooseColorEffect.java | 2 +- .../effects/common/CipherEffect.java | 4 +- .../counter/AddCountersSourceEffect.java | 4 +- .../counter/AddCountersTargetEffect.java | 6 +- .../mage/abilities/keyword/MorphAbility.java | 348 ++++++++++++++++++ Mage/src/mage/cards/Card.java | 17 +- Mage/src/mage/cards/CardImpl.java | 76 +++- Mage/src/mage/game/command/Commander.java | 5 + Mage/src/mage/game/command/Emblem.java | 5 + Mage/src/mage/game/permanent/Permanent.java | 13 +- .../mage/game/permanent/PermanentCard.java | 28 +- .../mage/game/permanent/PermanentImpl.java | 34 +- Mage/src/mage/game/stack/Spell.java | 43 ++- Mage/src/mage/game/stack/StackAbility.java | 5 + Mage/src/mage/players/PlayerImpl.java | 3 + .../target/common/TargetSpellOrPermanent.java | 2 +- Mage/src/mage/util/trace/TraceUtil.java | 2 +- .../common/CommanderCombatDamageWatcher.java | 2 +- .../mage/watchers/common/SoulbondWatcher.java | 6 +- 45 files changed, 929 insertions(+), 191 deletions(-) create mode 100644 Mage/src/mage/abilities/common/TurnFaceUpAbility.java create mode 100644 Mage/src/mage/abilities/common/TurnedFaceUpTriggeredAbility.java create mode 100644 Mage/src/mage/abilities/keyword/MorphAbility.java diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 6c065431cf..945bcbfed8 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.SwingConstants; +import mage.client.cards.Permanent; import mage.constants.CardType; import mage.constants.MageObjectType; @@ -221,7 +222,11 @@ public class GuiDisplayUtil { buffer.append(pt).append(""); buffer.append(""); if (!card.isControlledByOwner()) { - buffer.append("[only controlled] "); + if (card instanceof PermanentView) { + buffer.append("[").append(((PermanentView) card).getNameOwner()).append("] "); + } else { + buffer.append("[only controlled] "); + } } buffer.append(card.getMageObjectType().toString()).append(""); buffer.append(""); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 192b54e70f..8f3ecefadc 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -703,7 +703,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } setText(card); - boolean updateImage = !gameCard.getName().equals(card.getName()); // update after e.g. turning a night/day card + boolean updateImage = !gameCard.getName().equals(card.getName()) || gameCard.isFaceDown() != card.isFaceDown(); // update after e.g. turning a night/day card this.gameCard = card; String cardType = getType(card); @@ -776,7 +776,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void mouseEntered(MouseEvent e) { - if (gameCard.isFaceDown()) { + if (gameCard.hideInfo()) { return; } if (!popupShowing) { @@ -798,7 +798,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void mouseMoved(MouseEvent e) { - if (gameCard.isFaceDown()) { + if (gameCard.hideInfo()) { return; } data.component = this; @@ -807,7 +807,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void mouseExited(MouseEvent e) { - if (gameCard.isFaceDown()) { + if (gameCard.hideInfo()) { return; } if (getMousePosition(true) != null) { @@ -974,10 +974,11 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void mouseWheelMoved(MouseWheelEvent e) { - if (gameCard.isFaceDown()) { + if (gameCard.hideInfo()) { return; } data.component = this; callback.mouseWheelMoved(e, data); } + } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 0a3b4f050a..f530568fba 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -83,7 +83,7 @@ public class ImageCache { info.setToken(true); path = CardImageUtils.generateTokenImagePath(info); if (path == null) { - path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.tokenFrameFilename; + path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; } } else { path = CardImageUtils.generateImagePath(info); @@ -180,21 +180,21 @@ public class ImageCache { return getImage(key); } - /** - * Returns the Image corresponding to the Path - */ - private static BufferedImage getImageByPath(String path) { - if (path == null) { - return null; - } - TFile file = new TFile(path); - if (!file.exists()) { - log.warn("File does not exist: " + file.toString()); - return null; - } - return getWizardsCard(loadImage(file)); - - } +// /** +// * Returns the Image corresponding to the Path +// */ +// private static BufferedImage getImageByPath(String path) { +// if (path == null) { +// return null; +// } +// TFile file = new TFile(path); +// if (!file.exists()) { +// log.warn("File does not exist: " + file.toString()); +// return null; +// } +// return getWizardsCard(loadImage(file)); +// +// } /** * Returns the Image corresponding to the key diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java index 7aa48dfd18..e76fc6c4de 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -9,11 +9,12 @@ import org.mage.plugins.card.properties.SettingsManager; public class CardImageUtils { - private static HashMap pathCache = new HashMap(); + private static final HashMap pathCache = new HashMap<>(); /** * + * @param card * @return String if image exists, else null */ public static String generateTokenImagePath(CardDownloadData card) { diff --git a/Mage.Common/src/mage/view/AbilityView.java b/Mage.Common/src/mage/view/AbilityView.java index bfc6e6fe85..c26ae5a300 100644 --- a/Mage.Common/src/mage/view/AbilityView.java +++ b/Mage.Common/src/mage/view/AbilityView.java @@ -29,7 +29,6 @@ package mage.view; import java.util.ArrayList; -import mage.constants.CardType; import mage.ObjectColor; import mage.abilities.Ability; @@ -38,24 +37,25 @@ import mage.abilities.Ability; * @author BetaSteward_at_googlemail.com */ public class AbilityView extends CardView { + private static final long serialVersionUID = 1L; - private String sourceName; - private CardView sourceCard; + private final String sourceName; + private final CardView sourceCard; public AbilityView(Ability ability, String sourceName, CardView sourceCard) { this.id = ability.getId(); this.name = "Ability"; this.sourceName = sourceName; this.sourceCard = sourceCard; - this.rules = new ArrayList(); + this.rules = new ArrayList<>(); rules.add(ability.getRule()); this.power = ""; this.toughness = ""; this.loyalty = ""; - this.cardTypes = new ArrayList(); - this.subTypes = new ArrayList(); - this.superTypes = new ArrayList(); + this.cardTypes = new ArrayList<>(); + this.subTypes = new ArrayList<>(); + this.superTypes = new ArrayList<>(); this.color = new ObjectColor(); this.manaCost = ability.getManaCosts().getSymbols(); } diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 55a36c9181..a555a2ce6a 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -33,9 +33,12 @@ import java.util.List; import java.util.UUID; import mage.MageObject; import mage.ObjectColor; +import mage.abilities.Ability; import mage.abilities.Modes; import mage.abilities.SpellAbility; +import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; @@ -46,6 +49,7 @@ import mage.counters.Counter; import mage.counters.CounterType; import mage.game.command.Emblem; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; @@ -108,19 +112,48 @@ public class CardView extends SimpleCardView { protected boolean controlledByOwner = true; protected boolean rotate; + protected boolean hideInfo; // controlls if the tooltip window is shown (eg. controlled face down morph card) + + public CardView(Card card) { + this(card, null, false); + } public CardView(Card card, UUID cardId) { - this(card); + this(card, null, false); this.id = cardId; } - public CardView(Card card) { + /** + * + * @param card + * @param cardId + * @param controlled is the card view created for the card controller - used for morph cards to know which player may see information for the card + */ + public CardView(Card card, UUID cardId, boolean controlled) { super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.isFaceDown(), card.getUsesVariousArt(), card.getTokenSetCode()); - // no information available for face down cards - if (this.faceDown) { - fillEmpty(); - return; + // no information available for face down cards as long it's not a controlled face down morph card + // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 + if (card.isFaceDown()) { + if (card.isMorphCard()) { + // special handling for Morph cards + this.fillEmpty(card, controlled); + if (card instanceof Spell) { + if (controlled) { + this.name = card.getName(); + this.displayName = card.getName(); + } + this.power = "2"; + this.toughness = "2"; + this.rules.add("You may cast this card as a 2/2 face-down creature, with no text," + + " no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost."); + return; + } + } else { + this.fillEmpty(card, false); + this.hideInfo = true; + return; + } } SplitCard splitCard = null; @@ -241,8 +274,7 @@ public class CardView extends SimpleCardView { this.rules.add("Chosen mode: " + spell.getSpellAbility().getEffects().getText(modes.get(modeId))+""); } } - } - + } } public CardView(MageObject object) { @@ -309,7 +341,7 @@ public class CardView extends SimpleCardView { if (!empty) { throw new IllegalArgumentException("Not supported."); } - fillEmpty(); + fillEmpty(null, false); } @@ -319,8 +351,9 @@ public class CardView extends SimpleCardView { this.displayName = name; } - private void fillEmpty() { + private void fillEmpty(Card card, boolean controlled) { this.name = "Face Down"; + this.displayName = name; this.rules = new ArrayList<>(); this.power = ""; this.toughness = ""; @@ -331,9 +364,34 @@ public class CardView extends SimpleCardView { this.color = new ObjectColor(); this.manaCost = new ArrayList<>(); this.convertedManaCost = 0; - this.rarity = Rarity.COMMON; - this.expansionSetCode = ""; - this.cardNumber = 0; + + // the controller can see more information (e.g. enlarged image) than other players for face down cards (e.g. Morph played face down) + if (!controlled) { + this.rarity = Rarity.COMMON; + this.expansionSetCode = ""; + this.cardNumber = 0; + } else { + this.rarity = card.getRarity(); + } + + if (card != null) { + if (card instanceof Permanent) { + this.mageObjectType = MageObjectType.PERMANENT; + } else { + if (card.isCopy()) { + this.mageObjectType = MageObjectType.COPY_CARD; + } else { + this.mageObjectType = MageObjectType.CARD; + } + } + if (card instanceof PermanentToken) { + this.mageObjectType = MageObjectType.TOKEN; + } + if (card instanceof Spell) { + this.mageObjectType = MageObjectType.SPELL; + } + } + } CardView(Token token) { @@ -611,5 +669,9 @@ public class CardView extends SimpleCardView { public boolean isToRotate() { return rotate; } + + public boolean hideInfo() { + return hideInfo; + } } diff --git a/Mage.Common/src/mage/view/CombatGroupView.java b/Mage.Common/src/mage/view/CombatGroupView.java index 51ff8ac250..9a1d0bb748 100644 --- a/Mage.Common/src/mage/view/CombatGroupView.java +++ b/Mage.Common/src/mage/view/CombatGroupView.java @@ -43,10 +43,10 @@ import java.util.UUID; public class CombatGroupView implements Serializable { private static final long serialVersionUID = 1L; - private CardsView attackers = new CardsView(); - private CardsView blockers = new CardsView(); + private final CardsView attackers = new CardsView(); + private final CardsView blockers = new CardsView(); private String defenderName = ""; - private UUID defenderId; + private final UUID defenderId; public CombatGroupView(CombatGroup combatGroup, Game game) { Player player = game.getPlayer(combatGroup.getDefenderId()); @@ -55,19 +55,22 @@ public class CombatGroupView implements Serializable { } else { Permanent perm = game.getPermanent(combatGroup.getDefenderId()); - if (perm != null) + if (perm != null) { this.defenderName = perm.getName(); + } } this.defenderId = combatGroup.getDefenderId(); for (UUID id: combatGroup.getAttackers()) { Permanent attacker = game.getPermanent(id); - if (attacker != null) - attackers.put(id, new PermanentView(attacker, game.getCard(attacker.getId()))); + if (attacker != null) { + attackers.put(id, new PermanentView(attacker, game.getCard(attacker.getId()),null, game)); + } } for (UUID id: combatGroup.getBlockerOrder()) { Permanent blocker = game.getPermanent(id); - if (blocker != null) - blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()))); + if (blocker != null) { + blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game)); + } } } diff --git a/Mage.Common/src/mage/view/GameEndView.java b/Mage.Common/src/mage/view/GameEndView.java index 6255760c94..39b1fbf76c 100644 --- a/Mage.Common/src/mage/view/GameEndView.java +++ b/Mage.Common/src/mage/view/GameEndView.java @@ -65,7 +65,7 @@ public class GameEndView implements Serializable { int winner = 0; Player you = null; for (Player player: state.getPlayers().values()) { - PlayerView playerView = new PlayerView(player, state, game); + PlayerView playerView = new PlayerView(player, state, game, playerId); if (playerView.getPlayerId().equals(playerId)) { clientPlayer = playerView; you = player; diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 49c7ec01aa..8e833eb459 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -60,14 +60,14 @@ public class GameView implements Serializable { private static final long serialVersionUID = 1L; private final int priorityTime; - private final List players = new ArrayList(); + private final List players = new ArrayList<>(); private SimpleCardsView hand; private Map opponentHands; private final CardsView stack = new CardsView(); - private final List exiles = new ArrayList(); - private final List revealed = new ArrayList(); - private List lookedAt = new ArrayList(); - private final List combat = new ArrayList(); + private final List exiles = new ArrayList<>(); + private final List revealed = new ArrayList<>(); + private List lookedAt = new ArrayList<>(); + private final List combat = new ArrayList<>(); private final TurnPhase phase; private final PhaseStep step; private final UUID activePlayerId; @@ -78,11 +78,11 @@ public class GameView implements Serializable { private final boolean isPlayer; - public GameView(GameState state, Game game, boolean isPlayer) { - this.isPlayer = isPlayer; + public GameView(GameState state, Game game, UUID createdForPlayerId) { + this.isPlayer = createdForPlayerId != null; this.priorityTime = game.getPriorityTime(); for (Player player: state.getPlayers().values()) { - players.add(new PlayerView(player, state, game)); + players.add(new PlayerView(player, state, game, createdForPlayerId)); } for (StackObject stackObject: state.getStack()) { if (stackObject instanceof StackAbility) { @@ -127,7 +127,7 @@ public class GameView implements Serializable { } else { // Spell - stack.put(stackObject.getId(), new CardView((Spell)stackObject)); + stack.put(stackObject.getId(), new CardView((Spell)stackObject, null, stackObject.getControllerId().equals(createdForPlayerId))); checkPaid(stackObject.getId(), (Spell)stackObject); } //stackOrder.add(stackObject.getId()); diff --git a/Mage.Common/src/mage/view/PermanentView.java b/Mage.Common/src/mage/view/PermanentView.java index 7a3ef37bcf..f9e4efd2a2 100644 --- a/Mage.Common/src/mage/view/PermanentView.java +++ b/Mage.Common/src/mage/view/PermanentView.java @@ -31,9 +31,16 @@ package mage.view; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.TurnFaceUpAbility; +import mage.abilities.common.TurnedFaceUpTriggeredAbility; import mage.cards.Card; +import mage.constants.Rarity; +import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; +import mage.players.Player; /** * @@ -45,20 +52,21 @@ public class PermanentView extends CardView { private boolean tapped; private final boolean flipped; private final boolean phasedIn; - private final boolean faceUp; private final boolean summoningSickness; private final int damage; private List attachments; private final CardView original; private final boolean copy; + private final String nameOwner; // only filled if != controller + private final boolean controlled; - public PermanentView(Permanent permanent, Card card) { - super(permanent); + public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) { + super(permanent, null, permanent.getControllerId().equals(createdForPlayerId)); + this.controlled = permanent.getControllerId().equals(createdForPlayerId); this.rules = permanent.getRules(); this.tapped = permanent.isTapped(); this.flipped = permanent.isFlipped(); this.phasedIn = permanent.isPhasedIn(); - this.faceUp = permanent.isFaceUp(); this.summoningSickness = permanent.hasSummoningSickness(); this.damage = permanent.getDamage(); if (permanent.getAttachments().size() > 0) { @@ -90,6 +98,41 @@ public class PermanentView extends CardView { this.originalName = this.getName(); } } + if (!permanent.getOwnerId().equals(permanent.getControllerId())) { + Player owner = game.getPlayer(permanent.getOwnerId()); + if (owner != null) { + this.nameOwner = owner.getName(); + } else { + this.nameOwner = ""; + } + } else { + this.nameOwner = ""; + } + + if (permanent.isFaceDown() && permanent.isMorphCard()) { + // add morph rule text + if (card != null) { + if (controlled) { + for (Ability permanentAbility : permanent.getAbilities()) { + if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) { + this.rules.add(permanentAbility.getRule(true)); + } + if (permanentAbility instanceof TurnedFaceUpTriggeredAbility) { + this.rules.add(permanentAbility.getRule()); + } + } + this.name = card.getName(); + this.expansionSetCode = card.getExpansionSetCode(); + this.cardNumber = card.getCardNumber(); + } else { + this.rules.add("If the controller has priority, he or she may turn this permanent face up." + + " This is a special action; it doesn’t use the stack. To do this he or she pays the morph costs," + + " then turns this permanent face up."); + this.rarity = Rarity.COMMON; + } + + } + } } @@ -113,10 +156,6 @@ public class PermanentView extends CardView { return phasedIn; } - public boolean isFaceUp() { - return faceUp; - } - public boolean hasSummoningSickness(){ return summoningSickness; } @@ -132,4 +171,13 @@ public class PermanentView extends CardView { public void overrideTapped(boolean tapped) { this.tapped = tapped; } + + public String getNameOwner() { + return nameOwner; + } + + public boolean isControlled() { + return controlled; + } + } diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index d2b1e374be..4a70758182 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -67,7 +67,7 @@ public class PlayerView implements Serializable { private final int statesSavedSize; private final int priorityTimeLeft; - public PlayerView(Player player, GameState state, Game game) { + public PlayerView(Player player, GameState state, Game game, UUID createdForPlayerId) { this.playerId = player.getId(); this.name = player.getName(); this.life = player.getLife(); @@ -83,7 +83,7 @@ public class PlayerView implements Serializable { } for (Permanent permanent: state.getBattlefield().getAllPermanents()) { if (showInBattlefield(permanent, state)) { - PermanentView view = new PermanentView(permanent, game.getCard(permanent.getId())); + PermanentView view = new PermanentView(permanent, game.getCard(permanent.getId()), createdForPlayerId, game); battlefield.put(view.getId(), view); } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 373a293194..001028c718 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -597,7 +597,7 @@ public class GameController implements GameCallback { } else if (perms != null) { CardsView permsView = new CardsView(); for (Permanent perm: perms) { - permsView.put(perm.getId(), new PermanentView(perm, game.getCard(perm.getId()))); + permsView.put(perm.getId(), new PermanentView(perm, game.getCard(perm.getId()), playerId, game)); } getGameSession(playerId).target(question, permsView, targets, required, options); } else { diff --git a/Mage.Server/src/main/java/mage/server/game/GameSession.java b/Mage.Server/src/main/java/mage/server/game/GameSession.java index 36529e30dc..a395a155c5 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSession.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSession.java @@ -240,7 +240,7 @@ public class GameSession extends GameWatcher { public GameView getGameView() { Player player = game.getPlayer(playerId); player.setUserData(this.userData); - GameView gameView = new GameView(game.getState(), game, this.isPlayer); + GameView gameView = new GameView(game.getState(), game, playerId); gameView.setHand(new SimpleCardsView(player.getHand().getCards(game))); if (player.getPlayersUnderYourControl().size() > 0) { diff --git a/Mage.Server/src/main/java/mage/server/game/GameWatcher.java b/Mage.Server/src/main/java/mage/server/game/GameWatcher.java index f1847c775e..ab39c5d7a2 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWatcher.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWatcher.java @@ -120,7 +120,7 @@ public class GameWatcher { } public GameView getGameView() { - return new GameView(game.getState(), game, this.isPlayer); + return new GameView(game.getState(), game, null); } public GameEndView getGameEndView(UUID playerId, Match match) { diff --git a/Mage.Server/src/main/java/mage/server/game/ReplaySession.java b/Mage.Server/src/main/java/mage/server/game/ReplaySession.java index fd4fb6882c..16528aca92 100644 --- a/Mage.Server/src/main/java/mage/server/game/ReplaySession.java +++ b/Mage.Server/src/main/java/mage/server/game/ReplaySession.java @@ -54,7 +54,7 @@ public class ReplaySession implements GameCallback { replay.start(); User user = UserManager.getInstance().getUser(userId); if (user != null) { - user.fireCallback(new ClientCallback("replayInit", replay.getGame().getId(), new GameView(replay.next(), replay.getGame(), false))); + user.fireCallback(new ClientCallback("replayInit", replay.getGame().getId(), new GameView(replay.next(), replay.getGame(), null))); } } @@ -93,7 +93,7 @@ public class ReplaySession implements GameCallback { else { User user = UserManager.getInstance().getUser(userId); if (user != null) { - user.fireCallback(new ClientCallback("replayUpdate", replay.getGame().getId(), new GameView(state, game, false))); + user.fireCallback(new ClientCallback("replayUpdate", replay.getGame().getId(), new GameView(state, game, null))); } } } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/VorinclexVoiceOfHunger.java b/Mage.Sets/src/mage/sets/newphyrexia/VorinclexVoiceOfHunger.java index 66dfe4a032..819927dbb1 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/VorinclexVoiceOfHunger.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/VorinclexVoiceOfHunger.java @@ -28,9 +28,6 @@ package mage.sets.newphyrexia; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.Mana; import mage.abilities.Abilities; @@ -44,6 +41,9 @@ import mage.abilities.mana.TriggeredManaAbility; import mage.cards.CardImpl; import mage.choices.Choice; import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -99,10 +99,7 @@ class VorinclexTriggeredAbility1 extends TriggeredManaAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA && event.getPlayerId().equals(controllerId)) { - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(event.getSourceId(), Zone.BATTLEFIELD); - } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (permanent != null && permanent.getCardType().contains(CardType.LAND)) { getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId())); return true; @@ -136,7 +133,7 @@ class VorinclexEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { - Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); + Permanent land = game.getPermanentOrLKIBattlefield(this.targetPointer.getFirst(game, source)); Abilities mana = land.getAbilities().getManaAbilities(Zone.BATTLEFIELD); Mana types = new Mana(); for (ManaAbility ability: mana) { @@ -144,47 +141,50 @@ class VorinclexEffect extends ManaEffect { } Choice choice = new ChoiceImpl(true); choice.setMessage("Pick a mana color"); - if (types.getBlack() > 0) + if (types.getBlack() > 0) { choice.getChoices().add("Black"); - if (types.getRed() > 0) + } + if (types.getRed() > 0) { choice.getChoices().add("Red"); - if (types.getBlue() > 0) + } + if (types.getBlue() > 0) { choice.getChoices().add("Blue"); - if (types.getGreen() > 0) + } + if (types.getGreen() > 0) { choice.getChoices().add("Green"); - if (types.getWhite() > 0) + } + if (types.getWhite() > 0) { choice.getChoices().add("White"); - if (types.getColorless() > 0) + } + if (types.getColorless() > 0) { choice.getChoices().add("Colorless"); + } if (choice.getChoices().size() > 0) { Player player = game.getPlayer(source.getControllerId()); - if (choice.getChoices().size() == 1) + if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); - else + } else { player.choose(outcome, choice, game); - if (choice.getChoice().equals("Black")) { - player.getManaPool().addMana(Mana.BlackMana, game, source); - return true; } - else if (choice.getChoice().equals("Blue")) { - player.getManaPool().addMana(Mana.BlueMana, game, source); - return true; - } - else if (choice.getChoice().equals("Red")) { - player.getManaPool().addMana(Mana.RedMana, game, source); - return true; - } - else if (choice.getChoice().equals("Green")) { - player.getManaPool().addMana(Mana.GreenMana, game, source); - return true; - } - else if (choice.getChoice().equals("White")) { - player.getManaPool().addMana(Mana.WhiteMana, game, source); - return true; - } - else if (choice.getChoice().equals("Colorless")) { - player.getManaPool().addMana(Mana.ColorlessMana, game, source); - return true; + switch (choice.getChoice()) { + case "Black": + player.getManaPool().addMana(Mana.BlackMana, game, source); + return true; + case "Blue": + player.getManaPool().addMana(Mana.BlueMana, game, source); + return true; + case "Red": + player.getManaPool().addMana(Mana.RedMana, game, source); + return true; + case "Green": + player.getManaPool().addMana(Mana.GreenMana, game, source); + return true; + case "White": + player.getManaPool().addMana(Mana.WhiteMana, game, source); + return true; + case "Colorless": + player.getManaPool().addMana(Mana.ColorlessMana, game, source); + return true; } } return true; diff --git a/Mage/src/mage/MageObject.java b/Mage/src/mage/MageObject.java index 559c31f61b..1d6f97e37c 100644 --- a/Mage/src/mage/MageObject.java +++ b/Mage/src/mage/MageObject.java @@ -13,6 +13,7 @@ import mage.game.Game; public interface MageObject extends MageItem, Serializable { String getName(); + String getLogName(); String getImageName(); void setName(String name); diff --git a/Mage/src/mage/MageObjectImpl.java b/Mage/src/mage/MageObjectImpl.java index 6df594011c..a72f5a0a0d 100644 --- a/Mage/src/mage/MageObjectImpl.java +++ b/Mage/src/mage/MageObjectImpl.java @@ -94,6 +94,10 @@ public abstract class MageObjectImpl implements MageObject { public String getName() { return name; } + @Override + public String getLogName() { + return name; + } @Override public String getImageName() { diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 1b06c41144..ee1d2e3c05 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -821,14 +821,14 @@ public abstract class AbilityImpl implements Ability { if (object instanceof StackAbility) { Card card = game.getCard(((StackAbility) object).getSourceId()); if (card != null) { - sb.append(card.getName()); + sb.append(card.getLogName()); } else { sb.append(object.getName()); } } else { if (object instanceof Spell) { Spell spell = (Spell) object; - String castText = spell.getSpellAbility().toString(); + String castText = spell.getSpellCastText(game); sb.append((castText.startsWith("Cast ") ? castText.substring(5):castText)); if (spell.getFromZone() == Zone.GRAVEYARD) { sb.append(" from graveyard"); diff --git a/Mage/src/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/mage/abilities/TriggeredAbilityImpl.java index 1fac0c451a..ed8c6f60ea 100644 --- a/Mage/src/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/mage/abilities/TriggeredAbilityImpl.java @@ -82,8 +82,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge Player player = game.getPlayer(this.getControllerId()); StringBuilder sb = new StringBuilder(); if (object != null) { - sb.append("Use the following ability from ").append(object.getName()).append("? "); - sb.append(this.getRule(object.getName())); + sb.append("Use the following ability from ").append(object.getLogName()).append("? "); + sb.append(this.getRule(object.getLogName())); } else { sb.append("Use the following ability? ").append(this.getRule()); @@ -104,7 +104,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge MageObject object = game.getObject(sourceId); StringBuilder sb = new StringBuilder(); if (object != null) { - sb.append("Ability triggers: ").append(object.getName()).append(" - ").append(this.getRule(object.getName())); + sb.append("Ability triggers: ").append(object.getLogName()).append(" - ").append(this.getRule(object.getLogName())); } else { sb.append("Ability triggers: ").append(this.getRule()); } diff --git a/Mage/src/mage/abilities/common/TurnFaceUpAbility.java b/Mage/src/mage/abilities/common/TurnFaceUpAbility.java new file mode 100644 index 0000000000..a6fb365c00 --- /dev/null +++ b/Mage/src/mage/abilities/common/TurnFaceUpAbility.java @@ -0,0 +1,97 @@ +/* +* 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.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.costs.Costs; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ + +public class TurnFaceUpAbility extends ActivatedAbilityImpl { + + public TurnFaceUpAbility(Costs costs) { + super(Zone.BATTLEFIELD, new TurnFaceUpEffect(), costs); + this.usesStack = false; + this.setRuleVisible(false); // will be made visible only to controller in CardView + } + + public TurnFaceUpAbility(final TurnFaceUpAbility ability) { + super(ability); + } + + @Override + public TurnFaceUpAbility copy() { + return new TurnFaceUpAbility(this); + } +} + +class TurnFaceUpEffect extends OneShotEffect { + + public TurnFaceUpEffect() { + super(Outcome.Benefit); + this.staticText = "Turn this face-down permanent face up"; + } + + public TurnFaceUpEffect(final TurnFaceUpEffect effect) { + super(effect); + } + + @Override + public TurnFaceUpEffect copy() { + return new TurnFaceUpEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getSourceId()); + if (controller != null && card !=null) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null) { + if (sourcePermanent.turnFaceUp(game, source.getControllerId())) { + game.informPlayers(controller.getName() + " pays the costs of " + card.getLogName() + " to turn it face up"); + + return true; + } + } + } + return false; + } +} diff --git a/Mage/src/mage/abilities/common/TurnedFaceUpTriggeredAbility.java b/Mage/src/mage/abilities/common/TurnedFaceUpTriggeredAbility.java new file mode 100644 index 0000000000..eee1ddea21 --- /dev/null +++ b/Mage/src/mage/abilities/common/TurnedFaceUpTriggeredAbility.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +/** + * + * @author LevelX2 + */ + +public class TurnedFaceUpTriggeredAbility extends TriggeredAbilityImpl { + + public TurnedFaceUpTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + } + + public TurnedFaceUpTriggeredAbility(final TurnedFaceUpTriggeredAbility ability) { + super(ability); + } + + @Override + public TurnedFaceUpTriggeredAbility copy() { + return new TurnedFaceUpTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return EventType.TURNEDFACEUP.equals(event.getType()) && event.getTargetId().equals(this.getSourceId()); + } + + @Override + public String getRule() { + return "When {this} is turned face up, " + super.getRule(); + } +} diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 5868fd1a08..3fc5faa4a7 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -105,7 +105,7 @@ public class RemoveCounterCost extends CostImpl { } } choice.setChoices(choices); - choice.setMessage("Choose a counter to remove from " + permanent.getName()); + choice.setMessage("Choose a counter to remove from " + permanent.getLogName()); controller.choose(Outcome.UnboostCreature, choice, game); counterName = choice.getChoice(); } else { @@ -122,7 +122,7 @@ public class RemoveCounterCost extends CostImpl { int numberOfCountersSelected = 1; if (countersLeft > 1 && countersOnPermanent > 1) { numberOfCountersSelected = controller.getAmount(1, Math.min(countersLeft, countersOnPermanent), - new StringBuilder("Remove how many counters from ").append(permanent.getName()).toString(), game); + new StringBuilder("Remove how many counters from ").append(permanent.getLogName()).toString(), game); } permanent.removeCounters(counterName, numberOfCountersSelected, game); if (permanent.getCounters().getCount(counterName) == 0 ){ diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 27c75c4f16..ce9891352d 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -911,7 +911,7 @@ public class ContinuousEffects implements Serializable { for (Ability ability :(HashSet) entry.getValue()) { MageObject object = game.getObject(ability.getSourceId()); if (object != null) { - texts.add(ability.getRule(object.getName())); + texts.add(ability.getRule(object.getLogName())); } else { texts.add(((ReplacementEffect)entry.getKey()).getText(null)); } diff --git a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java index 47bde2e112..894f084ea1 100644 --- a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java @@ -113,7 +113,7 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl { if (controller == null || object == null) { return false; } - if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getName()).append("?").toString(), game)) { + if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), game)) { return false; } } diff --git a/Mage/src/mage/abilities/effects/PlaneswalkerRedirectionEffect.java b/Mage/src/mage/abilities/effects/PlaneswalkerRedirectionEffect.java index fd9b4c1831..0fdc0c986e 100644 --- a/Mage/src/mage/abilities/effects/PlaneswalkerRedirectionEffect.java +++ b/Mage/src/mage/abilities/effects/PlaneswalkerRedirectionEffect.java @@ -85,7 +85,7 @@ public class PlaneswalkerRedirectionEffect extends RedirectionEffect { game.informPlayers(new StringBuilder(player.getName()).append(" redirects ") .append(event.getAmount()) .append(" damage to ") - .append(game.getPermanent(redirectTarget.getFirstTarget()).getName()).toString()); + .append(game.getPermanent(redirectTarget.getFirstTarget()).getLogName()).toString()); return true; } } diff --git a/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java b/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java index 4dddd6e262..831567106a 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java @@ -58,7 +58,7 @@ public class ChooseColorEffect extends OneShotEffect { if (player != null && permanent != null) { ChoiceColor colorChoice = new ChoiceColor(); if (player.choose(outcome, colorChoice, game)) { - game.informPlayers(new StringBuilder(permanent.getName()).append(": ").append(player.getName()).append(" has chosen ").append(colorChoice.getChoice()).toString()); + game.informPlayers(new StringBuilder(permanent.getLogName()).append(": ").append(player.getName()).append(" has chosen ").append(colorChoice.getChoice()).toString()); game.getState().setValue(source.getSourceId() + "_color", colorChoice.getColor()); permanent.addInfo("chosen color", "Chosen color: " + colorChoice.getColor().getDescription() + ""); } diff --git a/Mage/src/mage/abilities/effects/common/CipherEffect.java b/Mage/src/mage/abilities/effects/common/CipherEffect.java index 6efdd4afd7..2799e795ee 100644 --- a/Mage/src/mage/abilities/effects/common/CipherEffect.java +++ b/Mage/src/mage/abilities/effects/common/CipherEffect.java @@ -94,12 +94,12 @@ public class CipherEffect extends OneShotEffect { Card sourceCard = game.getCard(source.getSourceId()); Permanent targetCreature = game.getPermanent(target.getFirstTarget()); if (targetCreature != null && sourceCard != null) { - String ruleText = new StringBuilder("you may cast a copy of ").append(sourceCard.getName()).append(" without paying its mana cost").toString(); + String ruleText = new StringBuilder("you may cast a copy of ").append(sourceCard.getLogName()).append(" without paying its mana cost").toString(); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new CipherStoreEffect(source.getSourceId(), ruleText), true); ContinuousEffect effect = new GainAbilityTargetEffect(ability, Duration.Custom); effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); game.addEffect(effect, source); - game.informPlayers(new StringBuilder(sourceCard.getName()).append(": Spell ciphered to ").append(targetCreature.getName()).toString()); + game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": Spell ciphered to ").append(targetCreature.getLogName()).toString()); return sourceCard.moveToExile(null, "", source.getId(), game); } else { return false; diff --git a/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java index b01150f545..814e8bbcc7 100644 --- a/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java @@ -104,7 +104,7 @@ public class AddCountersSourceEffect extends OneShotEffect { if (informPlayers) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(card.getName()).toString()); + game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(card.getLogName()).toString()); } } } @@ -124,7 +124,7 @@ public class AddCountersSourceEffect extends OneShotEffect { if (informPlayers) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(permanent.getName()).toString()); + game.informPlayers(new StringBuilder(player.getName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(permanent.getLogName()).toString()); } } } diff --git a/Mage/src/mage/abilities/effects/common/counter/AddCountersTargetEffect.java b/Mage/src/mage/abilities/effects/common/counter/AddCountersTargetEffect.java index a76fd36d81..e960907a6d 100644 --- a/Mage/src/mage/abilities/effects/common/counter/AddCountersTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/AddCountersTargetEffect.java @@ -93,10 +93,10 @@ public class AddCountersTargetEffect extends OneShotEffect { newCounter.add(amount.calculate(game, source)); permanent.addCounters(newCounter, game); affectedTargets ++; - game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ") + game.informPlayers(new StringBuilder(sourceObject.getLogName()).append(": ") .append(controller.getName()).append(" puts ") .append(counter.getCount()).append(" ").append(counter.getName().toLowerCase()) - .append(" counter on ").append(permanent.getName()).toString()); + .append(" counter on ").append(permanent.getLogName()).toString()); } } else { Player player = game.getPlayer(uuid); @@ -105,7 +105,7 @@ public class AddCountersTargetEffect extends OneShotEffect { newCounter.add(amount.calculate(game, source)); player.addCounters(newCounter, game); affectedTargets ++; - game.informPlayers(new StringBuilder(sourceObject.getName()).append(": ") + game.informPlayers(new StringBuilder(sourceObject.getLogName()).append(": ") .append(controller.getName()).append(" puts ") .append(counter.getCount()).append(" ").append(counter.getName().toLowerCase()) .append(" counter on ").append(player.getName()).toString()); diff --git a/Mage/src/mage/abilities/keyword/MorphAbility.java b/Mage/src/mage/abilities/keyword/MorphAbility.java new file mode 100644 index 0000000000..b83f805a3b --- /dev/null +++ b/Mage/src/mage/abilities/keyword/MorphAbility.java @@ -0,0 +1,348 @@ +/* +* 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.abilities.keyword; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.StaticAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.TurnFaceUpAbility; +import mage.abilities.common.TurnedFaceUpTriggeredAbility; +import mage.abilities.costs.AlternativeCost2; +import mage.abilities.costs.AlternativeCost2Impl; +import mage.abilities.costs.AlternativeSourceCosts; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continious.SourceEffect; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; + +/** + * 702.36. Morph + * + * 702.36a Morph is a static ability that functions in any zone from which you could play + * the card it’s on, and the morph effect works any time the card is face down. + * "Morph [cost]" means "You may cast this card as a 2/2 face-down creature, with no text, + * no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost." + * (See rule 707, "Face-Down Spells and Permanents.") + * + * 702.36b To cast a card using its morph ability, turn it face down. It becomes a 2/2 + * face-down creature card, with no text, no name, no subtypes, and no mana cost. Any + * effects or prohibitions that would apply to casting a card with these characteristics + * (and not the face-up card’s characteristics) are applied to casting this card. These + * values are the copiable values of that object’s characteristics. (See rule 613, + * "Interaction of Continuous Effects," and rule 706, "Copying Objects.") Put it onto the + * stack (as a face-down spell with the same characteristics), and pay {3} rather than pay + * its mana cost. This follows the rules for paying alternative costs. You can use morph + * to cast a card from any zone from which you could normally play it. When the spell + * resolves, it enters the battlefield with the same characteristics the spell had. The + * morph effect applies to the face-down object wherever it is, and it ends when the + * permanent is turned face up. # + * + * 702.36c You can’t cast a card face down if it doesn’t have morph. + * + * 702.36d If you have priority, you may turn a face-down permanent you control face up. + * This is a special action; it doesn’t use the stack (see rule 115). To do this, show + * all players what the permanent’s morph cost would be if it were face up, pay that cost, + * then turn the permanent face up. (If the permanent wouldn’t have a morph cost if it + * were face up, it can’t be turned face up this way.) The morph effect on it ends, and + * it regains its normal characteristics. Any abilities relating to the permanent entering + * the battlefield don’t trigger when it’s turned face up and don’t have any effect, because + * the permanent has already entered the battlefield. + * + * 702.36e See rule 707, "Face-Down Spells and Permanents," for more information on how to + * cast cards with morph. + * + * @author LevelX2 + */ + +public class MorphAbility extends StaticAbility implements AlternativeSourceCosts { + + protected static final String ABILITY_KEYWORD = "Morph"; + protected static final String REMINDER_TEXT = "(You may cast this face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)"; + protected String morphCostsText; + protected List alternateCosts = new LinkedList<>(); + + // needed to check activation status, if card changes zone after casting it + private int zoneChangeCounter = 0; + + public MorphAbility(Card card, Cost morphCost) { + this(card, createCosts(morphCost)); + } + + public MorphAbility(Card card, Costs morphCosts) { + super(Zone.HAND, null); + card.setMorphCard(true); + name = ABILITY_KEYWORD; + morphCostsText = morphCosts.getText(); + alternateCosts.add(new AlternativeCost2Impl(ABILITY_KEYWORD, REMINDER_TEXT, new GenericManaCost(3))); + + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts)); + ability.setRuleVisible(false); + card.addAbility(ability); + + } + + public MorphAbility(final MorphAbility ability) { + super(ability); + this.alternateCosts.addAll(ability.alternateCosts); + this.zoneChangeCounter = ability.zoneChangeCounter; + } + + private static Costs createCosts(Cost cost) { + Costs costs = new CostsImpl(); + costs.add(cost); + return costs; + } + + @Override + public MorphAbility copy() { + return new MorphAbility(this); + } + + public void resetMorph() { + for (AlternativeCost2 cost: alternateCosts) { + cost.reset(); + } + zoneChangeCounter = 0; + } + + @Override + public boolean isActivated(Ability ability, Game game) { + Card card = game.getCard(sourceId); + if (card != null && card.getZoneChangeCounter() <= zoneChangeCounter +1) { + for (AlternativeCost2 cost: alternateCosts) { + if(cost.isActivated(game)) { + return true; + } + } + } + return false; + } + + @Override + public boolean isAvailable(Ability source, Game game) { + return true; + } + + @Override + public boolean askToActivateAlternativeCosts(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + Player player = game.getPlayer(controllerId); + if (player != null) { + this.resetMorph(); + for (AlternativeCost2 alternateCastingCost: alternateCosts) { + if (alternateCastingCost.canPay(sourceId, controllerId, game) && + player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(alternateCastingCost.getText(true)).append(" ?").toString(), game)) { + + Spell spell = game.getStack().getSpell(ability.getId()); + if (spell != null) { + spell.setFaceDown(true); + } activateMorph(alternateCastingCost, game); + ability.getManaCostsToPay().clear(); + ability.getCosts().clear(); + for (Iterator it = ((Costs) alternateCastingCost).iterator(); it.hasNext();) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCostsImpl) { + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } else { + ability.getCosts().add(cost.copy()); + } + } + } else { + Card card = game.getCard(getSourceId()); // face down was set to true in PlayerImpl.cast, reset it here if not cast face down + if (card != null) { + card.setFaceDown(false); + } + Spell spell = game.getStack().getSpell(ability.getId()); + if (spell != null) { + spell.setFaceDown(false); + } + } + } + } + } + return isActivated(ability, game); + } + + private void activateMorph(AlternativeCost2 cost, Game game) { + cost.activate(); + // remember zone change counter + if (zoneChangeCounter == 0) { + Card card = game.getCard(getSourceId()); + if (card != null) { + zoneChangeCounter = card.getZoneChangeCounter(); + } else { + throw new IllegalArgumentException("Morph source card not found"); + } + } + } + + @Override + public String getRule() { + StringBuilder sb = new StringBuilder(); + int numberCosts = 0; + String remarkText = ""; + for (AlternativeCost2 alternateCastingCost: alternateCosts) { + if (numberCosts == 0) { + sb.append(alternateCastingCost.getText(false)); + remarkText = alternateCastingCost.getReminderText(); + } else { + sb.append(" and/or ").append(alternateCastingCost.getText(true)); + } + ++numberCosts; + } + if (numberCosts == 1) { + sb.append(" ").append(remarkText); + } + + return sb.toString(); + } + + @Override + public String getCastMessageSuffix(Game game) { + StringBuilder sb = new StringBuilder(); + int position = 0; + for (AlternativeCost2 cost : alternateCosts) { + if (cost.isActivated(game)) { + sb.append(cost.getCastSuffixMessage(position)); + ++position; + } + } + return sb.toString(); + } +} + +/** + * This effect lets the creature always be a 2/2 face-down creature, with no text, + * no name, no subtypes, and no mana cost, if it's fac down on the battlefield. + * And it adds the MorphTurnFaceUpAbility ability. + * TODO: Check if it's better to create this effect always as a creature on the battelfield turns face down or + * a creature enters the battlefield face down. Then the effect could be removed as the permanent turns face up. + * + * @author LevelX2 + */ + +class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implements SourceEffect { + + protected int zoneChangeCounter; + protected Ability turnFaceUpAbility = null; + + public BecomesFaceDownCreatureEffect(Costs morphCosts) { + super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); + this.zoneChangeCounter = Integer.MIN_VALUE; + if (morphCosts != null) { + this.turnFaceUpAbility = new TurnFaceUpAbility(morphCosts); + } + staticText = "{this} becomes a 2/2 face-down creature, with no text, no name, no subtypes, and no mana cost"; + } + + public BecomesFaceDownCreatureEffect(final BecomesFaceDownCreatureEffect effect) { + super(effect); + this.zoneChangeCounter = effect.zoneChangeCounter; + this.turnFaceUpAbility = effect.turnFaceUpAbility.copy(); + } + + @Override + public BecomesFaceDownCreatureEffect copy() { + return new BecomesFaceDownCreatureEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.isFaceDown()) { + switch (layer) { + case TypeChangingEffects_4: + permanent.setName(""); + permanent.getSubtype().clear(); + permanent.getManaCost().clear(); + break; + case ColorChangingEffects_5: + permanent.getColor().setColor(new ObjectColor()); + break; + case AbilityAddingRemovingEffects_6: + List abilities = new ArrayList<>(); + for (Ability ability : permanent.getAbilities()) { + // TODO: Add flag "works also face down" to ability and use it to control ability removement instead of instanceof check + if (ability instanceof TurnedFaceUpTriggeredAbility) { + ability.setRuleVisible(false); + continue; + } + if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) { + if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) { + continue; + } + } + abilities.add(ability); + } + permanent.getAbilities().removeAll(abilities); + if (turnFaceUpAbility != null) { + permanent.addAbility(turnFaceUpAbility, game); + } + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(2); + permanent.getToughness().setValue(2); + } + + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; + } + +} diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index 7904a7ac7c..fbc46b116e 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -28,21 +28,20 @@ package mage.cards; -import mage.constants.Rarity; -import mage.constants.Zone; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpellAbility; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; import mage.watchers.Watcher; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - public interface Card extends MageObject { UUID getOwnerId(); @@ -62,6 +61,8 @@ public interface Card extends MageObject { void setExpansionSetCode(String expansionSetCode); void setFaceDown(boolean value); boolean isFaceDown(); + boolean turnFaceUp(Game game, UUID playerId); + boolean turnFaceDown(Game game, UUID playerId); boolean isFlipCard(); String getFlipCardName(); void setFlipCard(boolean flipCard); @@ -132,6 +133,10 @@ public interface Card extends MageObject { void removeCounters(String name, int amount, Game game); void removeCounters(Counter counter, Game game); + + void setMorphCard(boolean morphCard); + boolean isMorphCard(); + @Override Card copy(); } diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index 50b2482cd1..79a105c7e0 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -28,18 +28,38 @@ package mage.cards; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; import mage.abilities.Ability; import mage.abilities.PlayLandAbility; import mage.abilities.SpellAbility; import mage.abilities.mana.ManaAbility; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.SpellAbilityType; +import mage.constants.TimingRule; +import mage.constants.Zone; +import static mage.constants.Zone.BATTLEFIELD; +import static mage.constants.Zone.COMMAND; +import static mage.constants.Zone.EXILED; +import static mage.constants.Zone.GRAVEYARD; +import static mage.constants.Zone.HAND; +import static mage.constants.Zone.LIBRARY; +import static mage.constants.Zone.OUTSIDE; +import static mage.constants.Zone.PICK; +import static mage.constants.Zone.STACK; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; +import mage.game.command.Commander; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.PermanentCard; @@ -47,13 +67,6 @@ import mage.game.stack.Spell; import mage.watchers.Watcher; import org.apache.log4j.Logger; -import java.lang.reflect.Constructor; -import java.util.*; -import mage.MageObject; -import mage.constants.SpellAbilityType; -import mage.constants.TimingRule; -import mage.game.command.Commander; - public abstract class CardImpl extends MageObjectImpl implements Card { private static final long serialVersionUID = 1L; @@ -77,6 +90,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected boolean usesVariousArt = false; protected Counters counters; protected boolean splitCard; + protected boolean morphCard; public CardImpl(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { this(ownerId, cardNumber, name, rarity, cardTypes, costs, SpellAbilityType.BASE); @@ -103,6 +117,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } this.usesVariousArt = Character.isDigit(this.getClass().getName().charAt(this.getClass().getName().length()-1)); this.counters = new Counters(); + this.morphCard = false; } protected CardImpl(UUID ownerId, String name) { @@ -144,6 +159,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { splitCard = card.splitCard; usesVariousArt = card.usesVariousArt; counters = card.counters.copy(); + morphCard = card.isMorphCard(); } @Override @@ -532,12 +548,36 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public void setFaceDown(boolean value) { - this.faceDown = value; + faceDown = value; } @Override public boolean isFaceDown() { - return this.faceDown; + return faceDown; + } + + @Override + public boolean turnFaceUp(Game game, UUID playerId) { + GameEvent event = GameEvent.getEvent(GameEvent.EventType.TURNFACEUP, getId(), playerId); + if (!game.replaceEvent(event)) { + setFaceDown(false); + game.getCard(objectId).setFaceDown(false); // Another instance? + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.TURNEDFACEUP, getId(), playerId)); + return true; + } + return false; + } + + @Override + public boolean turnFaceDown(Game game, UUID playerId) { + GameEvent event = GameEvent.getEvent(GameEvent.EventType.TURNFACEDOWN, getId(), playerId); + if (!game.replaceEvent(event)) { + setFaceDown(true); + game.getCard(objectId).setFaceDown(true); // Another instance? + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.TURNEDFACEDOWN, getId(), playerId)); + return true; + } + return false; } @Override @@ -673,4 +713,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card { public void removeCounters(Counter counter, Game game) { removeCounters(counter.getName(), counter.getCount(), game); } + + @Override + public void setMorphCard(boolean morphCard) { + this.morphCard = morphCard; + } + + @Override + public boolean isMorphCard() { + return morphCard; + } + + } diff --git a/Mage/src/mage/game/command/Commander.java b/Mage/src/mage/game/command/Commander.java index db5ca941a2..20d81ad63a 100644 --- a/Mage/src/mage/game/command/Commander.java +++ b/Mage/src/mage/game/command/Commander.java @@ -98,6 +98,11 @@ public class Commander implements CommandObject{ return card.getName(); } + @Override + public String getLogName() { + return card.getName(); + } + @Override public void setName(String name) { diff --git a/Mage/src/mage/game/command/Emblem.java b/Mage/src/mage/game/command/Emblem.java index b87aedf908..aae437fcda 100644 --- a/Mage/src/mage/game/command/Emblem.java +++ b/Mage/src/mage/game/command/Emblem.java @@ -97,6 +97,11 @@ public class Emblem implements CommandObject { return name; } + @Override + public String getLogName() { + return name; + } + @Override public String getImageName() { return this.name; diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 464b628236..88ab3cc3a2 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -29,16 +29,15 @@ package mage.game.permanent; import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; +import mage.constants.Zone; import mage.game.Controllable; import mage.game.Game; -import java.util.List; -import java.util.UUID; -import mage.constants.Zone; - public interface Permanent extends Card, Controllable { boolean isTapped(); @@ -68,10 +67,6 @@ public interface Permanent extends Card, Controllable { boolean phaseIn(Game game); boolean phaseOut(Game game); - boolean isFaceUp(); - boolean turnFaceUp(Game game); - boolean turnFaceDown(Game game); - boolean isMonstrous(); void setMonstrous(boolean value); @@ -208,12 +203,14 @@ public interface Permanent extends Card, Controllable { /** * Returns connected cards. * Very similar to Imprint except that it is for internal use only. + * @param key * @return */ List getConnectedCards(String key); /** * Clear all connected cards. + * @param key */ void clearConnectedCards(String key); diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index aea4010a2f..a18916a29f 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -29,17 +29,15 @@ package mage.game.permanent; import java.util.ArrayList; -import mage.constants.Zone; +import java.util.UUID; import mage.cards.Card; import mage.cards.LevelerCard; +import mage.constants.Zone; import mage.game.Game; +import mage.game.command.Commander; import mage.game.events.ZoneChangeEvent; import mage.players.Player; -import java.util.UUID; -import mage.game.command.Commander; - - /** * @author BetaSteward_at_googlemail.com */ @@ -116,6 +114,8 @@ public class PermanentCard extends PermanentImpl { } this.flipCard = card.isFlipCard(); this.flipCardName = card.getFlipCardName(); + this.morphCard = card.isMorphCard(); + this.faceDown = card.isFaceDown(); } public Card getCard() { @@ -205,4 +205,22 @@ public class PermanentCard extends PermanentImpl { return this.maxLevelCounters; } + @Override + public boolean turnFaceUp(Game game, UUID playerId) { + if (super.turnFaceUp(game, playerId)) { + card.setFaceDown(false); + return true; + } + return false; + } + + @Override + public boolean turnFaceDown(Game game, UUID playerId) { + if (super.turnFaceDown(game, playerId)) { + card.setFaceDown(true); + return true; + } + return false; + } + } diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 4300e33da3..10458f50b2 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -85,7 +85,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected boolean controlledFromStartOfControllerTurn; protected int turnsOnBattlefield; protected boolean phasedIn = true; - protected boolean faceUp = true; protected boolean attacking; protected int blocking; // number of creatures the permanent can block @@ -127,7 +126,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.controlledFromStartOfControllerTurn = permanent.controlledFromStartOfControllerTurn; this.turnsOnBattlefield = permanent.turnsOnBattlefield; this.phasedIn = permanent.phasedIn; - this.faceUp = permanent.faceUp; this.attacking = permanent.attacking; this.blocking = permanent.blocking; this.maxBlocks = permanent.maxBlocks; @@ -213,7 +211,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public void addAbility(Ability ability, UUID sourceId, Game game) { if (!abilities.containsKey(ability.getId())) { Ability copyAbility = ability.copy(); - copyAbility.newId(); // needed so that sourc can get an ability multiple times (e.g. Raging Ravine) + copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine) copyAbility.setControllerId(controllerId); copyAbility.setSourceId(objectId); game.getState().addAbility(copyAbility, sourceId, this); @@ -411,23 +409,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return false; } - @Override - public boolean isFaceUp() { - return faceUp; - } - - @Override - public boolean turnFaceUp(Game game) { - //TODO: implement this - return false; - } - - @Override - public boolean turnFaceDown(Game game) { - //TODO: implement this - return false; - } - public void removeSummoningSickness() { this.controlledFromStartOfControllerTurn = true; } @@ -1064,4 +1045,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public void clearPairedCard() { this.pairedCard = null; } + + @Override + public String getLogName() { + if (name.isEmpty()) { + if (isFaceDown()) { + return "face down creature"; + } else { + return "a creature without name"; + } + } + return name; + } + } diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 10655d6a7b..a1f999766d 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -39,6 +39,7 @@ import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.SpellAbility; +import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; @@ -46,6 +47,7 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.PostResolveEffect; import mage.abilities.keyword.BestowAbility; +import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; @@ -56,6 +58,7 @@ import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; +import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -154,6 +157,17 @@ public class Spell implements StackObject, Card { return sb.append(ability.getGameLogMessage(game)).toString(); } + public String getSpellCastText(Game game) { + for (Ability spellAbility : (Abilities) getAbilities()) { + if (spellAbility instanceof MorphAbility + && ((AlternativeSourceCosts) spellAbility).isActivated(getSpellAbility(), game)) { + return "a card face down"; + } + } + return getSpellAbility().toString(); + + } + @Override public boolean resolve(Game game) { boolean result; @@ -503,6 +517,11 @@ public class Spell implements StackObject, Card { public String getName() { return card.getName(); } + + @Override + public String getLogName() { + return card.getName(); + } @Override public String getImageName() { @@ -649,12 +668,22 @@ public class Spell implements StackObject, Card { @Override public void setFaceDown(boolean value) { - throw new RuntimeException("Not implemented."); + card.setFaceDown(value); + } + + @Override + public boolean turnFaceUp(Game game, UUID playerId) { + return card.turnFaceUp(game, playerId); + } + + @Override + public boolean turnFaceDown(Game game, UUID playerId) { + return card.turnFaceDown(game, playerId); } @Override public boolean isFaceDown() { - return false; + return card.isFaceDown(); } @Override @@ -893,4 +922,14 @@ public class Spell implements StackObject, Card { return card; } + @Override + public void setMorphCard(boolean morphCard) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean isMorphCard() { + return card.isMorphCard(); + } + } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index b70967a2a1..2f2defe72b 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -112,6 +112,11 @@ public class StackAbility implements StackObject, Ability { return name; } + @Override + public String getLogName() { + return name; + } + @Override public String getImageName() { return name; diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index c3a1d89919..e4c6d01ec2 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -737,6 +737,9 @@ public abstract class PlayerImpl implements Player, Serializable { //20091005 - 601.2a Card card = game.getCard(ability.getSourceId()); if (card != null) { + if (card.isMorphCard()) { + card.setFaceDown(true); + } if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId))) { int bookmark = game.bookmarkState(); Zone fromZone = game.getState().getZone(card.getId()); diff --git a/Mage/src/mage/target/common/TargetSpellOrPermanent.java b/Mage/src/mage/target/common/TargetSpellOrPermanent.java index 09e81c48f9..98c3ebdd4b 100644 --- a/Mage/src/mage/target/common/TargetSpellOrPermanent.java +++ b/Mage/src/mage/target/common/TargetSpellOrPermanent.java @@ -236,7 +236,7 @@ public class TargetSpellOrPermanent extends TargetImpl { for (UUID targetId: getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { - sb.append(permanent.getName()).append(" "); + sb.append(permanent.getLogName()).append(" "); } else { Spell spell = game.getStack().getSpell(targetId); diff --git a/Mage/src/mage/util/trace/TraceUtil.java b/Mage/src/mage/util/trace/TraceUtil.java index 325dcea67c..2299f09273 100644 --- a/Mage/src/mage/util/trace/TraceUtil.java +++ b/Mage/src/mage/util/trace/TraceUtil.java @@ -126,7 +126,7 @@ public class TraceUtil { String uuid = "[" + UUID.randomUUID() + "] "; log.error(uuid+"Tracing game state..."); if (blocker != null) { - log.error(uuid+blocker.getName() + " could block " + attacker.getName()); + log.error(uuid+blocker.getLogName() + " could block " + attacker.getLogName()); } log.error(uuid); diff --git a/Mage/src/mage/watchers/common/CommanderCombatDamageWatcher.java b/Mage/src/mage/watchers/common/CommanderCombatDamageWatcher.java index c3d42a65ba..44515342a0 100644 --- a/Mage/src/mage/watchers/common/CommanderCombatDamageWatcher.java +++ b/Mage/src/mage/watchers/common/CommanderCombatDamageWatcher.java @@ -84,7 +84,7 @@ public class CommanderCombatDamageWatcher extends Watcher { Player player = game.getPlayer(playerUUID); MageObject commander = game.getObject(sourceId); if (player != null && commander != null){ - game.informPlayers(commander.getName() + " did " + damage + " combat damage to " + player.getName() + " during the game."); + game.informPlayers(commander.getLogName() + " did " + damage + " combat damage to " + player.getName() + " during the game."); this.addCardInfoToCommander(game); } } diff --git a/Mage/src/mage/watchers/common/SoulbondWatcher.java b/Mage/src/mage/watchers/common/SoulbondWatcher.java index 7164f0fb69..e81f8d9984 100644 --- a/Mage/src/mage/watchers/common/SoulbondWatcher.java +++ b/Mage/src/mage/watchers/common/SoulbondWatcher.java @@ -88,7 +88,7 @@ public class SoulbondWatcher extends Watcher { if (chosen != null) { chosen.setPairedCard(permanent.getId()); permanent.setPairedCard(chosen.getId()); - game.informPlayers(new StringBuilder(controller.getName()).append(" souldbonds ").append(permanent.getName()).append(" with ").append(chosen.getName()).toString()); + game.informPlayers(new StringBuilder(controller.getName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString()); } } } @@ -109,10 +109,10 @@ public class SoulbondWatcher extends Watcher { Cards cards = new CardsImpl(Zone.PICK); cards.add(chosen); controller.lookAtCards("Soulbond", cards, game); - if (controller.chooseUse(Outcome.Benefit, "Use Soulbond for recent " + permanent.getName() + "?", game)) { + if (controller.chooseUse(Outcome.Benefit, "Use Soulbond for recent " + permanent.getLogName() + "?", game)) { chosen.setPairedCard(permanent.getId()); permanent.setPairedCard(chosen.getId()); - game.informPlayers(new StringBuilder(controller.getName()).append(" souldbonds ").append(permanent.getName()).append(" with ").append(chosen.getName()).toString()); + game.informPlayers(new StringBuilder(controller.getName()).append(" souldbonds ").append(permanent.getLogName()).append(" with ").append(chosen.getName()).toString()); break; } }