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 6c065431cfb..945bcbfed82 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 192b54e70f7..8f3ecefadc4 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 0a3b4f050aa..f530568fba3 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 7aa48dfd183..e76fc6c4de3 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 bfc6e6fe858..c26ae5a3000 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 55a36c91814..a555a2ce6a9 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 51ff8ac250b..9a1d0bb7481 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 6255760c942..39b1fbf76c4 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 49c7ec01aa9..8e833eb459c 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 7a3ef37bcfb..f9e4efd2a26 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 doesnt 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 d2b1e374be1..4a70758182f 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 373a293194e..001028c7185 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 36529e30dcf..a395a155c5b 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 f1847c775e2..ab39c5d7a2b 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 fd4fb6882cd..16528aca921 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 66dfe4a0324..819927dbb11 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 559c31f61ba..1d6f97e37c1 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 6df594011c7..a72f5a0a0d4 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 1b06c41144b..ee1d2e3c055 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 1fac0c451a1..ed8c6f60eae 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 00000000000..a6fb365c002
--- /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 00000000000..eee1ddea21f
--- /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 5868fd1a08c..3fc5faa4a77 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 27c75c4f16e..ce9891352d4 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 47bde2e1125..894f084ea10 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 fd9b4c18317..0fdc0c986e8 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 4dddd6e2627..831567106a9 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 6efdd4afd76..2799e795eec 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 b01150f5454..814e8bbcc74 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 a76fd36d817..e960907a6d4 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 00000000000..b83f805a3b1
--- /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 its 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 cards characteristics) are applied to casting this card. These
+ * values are the copiable values of that objects 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 cant cast a card face down if it doesnt 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 doesnt use the stack (see rule 115). To do this, show
+ * all players what the permanents morph cost would be if it were face up, pay that cost,
+ * then turn the permanent face up. (If the permanent wouldnt have a morph cost if it
+ * were face up, it cant 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 dont trigger when its turned face up and dont 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 7904a7ac7ca..fbc46b116ef 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 50b2482cd19..79a105c7e00 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 db5ca941a2f..20d81ad63a2 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 b87aedf9085..aae437fcda3 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 464b6282364..88ab3cc3a28 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 aea4010a2f9..a18916a29fd 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 4300e33da39..10458f50b2a 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 10655d6a7b9..a1f999766d5 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 b70967a2a1d..2f2defe72b6 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 c3a1d899197..e4c6d01ec2d 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 09e81c48f97..98c3ebdd4b2 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 325dcea67ca..2299f092731 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 c3d42a65ba9..44515342a02 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 7164f0fb698..e81f8d99844 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;
}
}