* Added Morph ability.

This commit is contained in:
LevelX2 2014-06-09 17:53:55 +02:00
parent b1de70a3bf
commit d244551e3b
45 changed files with 929 additions and 191 deletions

View file

@ -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,8 +222,12 @@ public class GuiDisplayUtil {
buffer.append(pt).append("</b></td>");
buffer.append("<td align='right'>");
if (!card.isControlledByOwner()) {
if (card instanceof PermanentView) {
buffer.append("[").append(((PermanentView) card).getNameOwner()).append("] ");
} else {
buffer.append("[only controlled] ");
}
}
buffer.append(card.getMageObjectType().toString()).append("</td>");
buffer.append("</tr></table>");

View file

@ -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);
}
}

View file

@ -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

View file

@ -9,11 +9,12 @@ import org.mage.plugins.card.properties.SettingsManager;
public class CardImageUtils {
private static HashMap<CardDownloadData, String> pathCache = new HashMap<CardDownloadData, String>();
private static final HashMap<CardDownloadData, String> pathCache = new HashMap<>();
/**
*
* @param card
* @return String if image exists, else null
*/
public static String generateTokenImagePath(CardDownloadData card) {

View file

@ -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<String>();
this.rules = new ArrayList<>();
rules.add(ability.getRule());
this.power = "";
this.toughness = "";
this.loyalty = "";
this.cardTypes = new ArrayList<CardType>();
this.subTypes = new ArrayList<String>();
this.superTypes = new ArrayList<String>();
this.cardTypes = new ArrayList<>();
this.subTypes = new ArrayList<>();
this.superTypes = new ArrayList<>();
this.color = new ObjectColor();
this.manaCost = ability.getManaCosts().getSymbols();
}

View file

@ -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,20 +112,49 @@ 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();
// 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;
if (card.isSplitCard()) {
@ -242,7 +275,6 @@ public class CardView extends SimpleCardView {
}
}
}
}
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;
// 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) {
@ -612,4 +670,8 @@ public class CardView extends SimpleCardView {
return rotate;
}
public boolean hideInfo() {
return hideInfo;
}
}

View file

@ -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));
}
}
}

View file

@ -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;

View file

@ -60,14 +60,14 @@ public class GameView implements Serializable {
private static final long serialVersionUID = 1L;
private final int priorityTime;
private final List<PlayerView> players = new ArrayList<PlayerView>();
private final List<PlayerView> players = new ArrayList<>();
private SimpleCardsView hand;
private Map<String, SimpleCardsView> opponentHands;
private final CardsView stack = new CardsView();
private final List<ExileView> exiles = new ArrayList<ExileView>();
private final List<RevealedView> revealed = new ArrayList<RevealedView>();
private List<LookedAtView> lookedAt = new ArrayList<LookedAtView>();
private final List<CombatGroupView> combat = new ArrayList<CombatGroupView>();
private final List<ExileView> exiles = new ArrayList<>();
private final List<RevealedView> revealed = new ArrayList<>();
private List<LookedAtView> lookedAt = new ArrayList<>();
private final List<CombatGroupView> 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());

View file

@ -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<UUID> 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;
}
}

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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) {

View file

@ -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)));
}
}
}

View file

@ -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<ManaAbility> mana = land.getAbilities().getManaAbilities(Zone.BATTLEFIELD);
Mana types = new Mana();
for (ManaAbility ability: mana) {
@ -144,45 +141,48 @@ 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")) {
}
switch (choice.getChoice()) {
case "Black":
player.getManaPool().addMana(Mana.BlackMana, game, source);
return true;
}
else if (choice.getChoice().equals("Blue")) {
case "Blue":
player.getManaPool().addMana(Mana.BlueMana, game, source);
return true;
}
else if (choice.getChoice().equals("Red")) {
case "Red":
player.getManaPool().addMana(Mana.RedMana, game, source);
return true;
}
else if (choice.getChoice().equals("Green")) {
case "Green":
player.getManaPool().addMana(Mana.GreenMana, game, source);
return true;
}
else if (choice.getChoice().equals("White")) {
case "White":
player.getManaPool().addMana(Mana.WhiteMana, game, source);
return true;
}
else if (choice.getChoice().equals("Colorless")) {
case "Colorless":
player.getManaPool().addMana(Mana.ColorlessMana, game, source);
return true;
}

View file

@ -13,6 +13,7 @@ import mage.game.Game;
public interface MageObject extends MageItem, Serializable {
String getName();
String getLogName();
String getImageName();
void setName(String name);

View file

@ -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() {

View file

@ -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");

View file

@ -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());
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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 ){

View file

@ -911,7 +911,7 @@ public class ContinuousEffects implements Serializable {
for (Ability ability :(HashSet<Ability>) 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));
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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", "<font color = 'blue'>Chosen color: " + colorChoice.getColor().getDescription() + "</font>");
}

View file

@ -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;

View file

@ -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());
}
}
}

View file

@ -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());

View file

@ -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<AlternativeCost2> 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<Ability> 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;
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -97,6 +97,11 @@ public class Emblem implements CommandObject {
return name;
}
@Override
public String getLogName() {
return name;
}
@Override
public String getImageName() {
return this.name;

View file

@ -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<UUID> getConnectedCards(String key);
/**
* Clear all connected cards.
* @param key
*/
void clearConnectedCards(String key);

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<Ability>) 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;
@ -504,6 +518,11 @@ public class Spell implements StackObject, Card {
return card.getName();
}
@Override
public String getLogName() {
return card.getName();
}
@Override
public String getImageName() {
return card.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();
}
}

View file

@ -112,6 +112,11 @@ public class StackAbility implements StackObject, Ability {
return name;
}
@Override
public String getLogName() {
return name;
}
@Override
public String getImageName() {
return name;

View file

@ -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());

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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;
}
}