/*
* 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.game.stack;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.Mana;
import mage.ObjectColor;
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;
import mage.abilities.keyword.BestowAbility;
import mage.abilities.keyword.MorphAbility;
import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.cards.FrameStyle;
import mage.cards.SplitCard;
import mage.constants.*;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.game.Game;
import mage.game.GameState;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
import mage.util.GameLog;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class Spell extends StackObjImpl implements Card {
private final List<Card> spellCards = new ArrayList<>();
private final List<SpellAbility> spellAbilities = new ArrayList<>();
private final Card card;
private final ObjectColor color;
private final ObjectColor frameColor;
private final FrameStyle frameStyle;
private final SpellAbility ability;
private final Zone fromZone;
private final UUID id;
private UUID controllerId;
private boolean copiedSpell;
private boolean faceDown;
private boolean countered;
private boolean doneActivatingManaAbilities; // if this is true, the player is no longer allowed to pay the spell costs with activating of mana abilies
public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
this.card = card;
this.color = card.getColor(null).copy();
this.frameColor = card.getFrameColor(null).copy();
this.frameStyle = card.getFrameStyle();
id = ability.getId();
this.ability = ability;
this.ability.setControllerId(controllerId);
if (ability.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
spellCards.add(((SplitCard) card).getLeftHalfCard());
spellAbilities.add(((SplitCard) card).getLeftHalfCard().getSpellAbility().copy());
spellCards.add(((SplitCard) card).getRightHalfCard());
spellAbilities.add(((SplitCard) card).getRightHalfCard().getSpellAbility().copy());
} else {
spellCards.add(card);
spellAbilities.add(ability);
}
this.controllerId = controllerId;
this.fromZone = fromZone;
this.countered = false;
this.doneActivatingManaAbilities = false;
}
public Spell(final Spell spell) {
this.id = spell.id;
for (SpellAbility spellAbility : spell.spellAbilities) {
this.spellAbilities.add(spellAbility.copy());
}
for (Card spellCard : spell.spellCards) {
this.spellCards.add(spellCard.copy());
}
if (spell.spellAbilities.get(0).equals(spell.ability)) {
this.ability = this.spellAbilities.get(0);
} else {
this.ability = spell.ability.copy();
}
if (spell.spellCards.get(0).equals(spell.card)) {
this.card = spellCards.get(0);
} else {
this.card = spell.card.copy();
}
this.controllerId = spell.controllerId;
this.fromZone = spell.fromZone;
this.copiedSpell = spell.copiedSpell;
this.faceDown = spell.faceDown;
this.color = spell.color.copy();
this.frameColor = spell.color.copy();
this.frameStyle = spell.frameStyle;
this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities;
}
public boolean activate(Game game, boolean noMana) {
if (!spellAbilities.get(0).activate(game, noMana)) {
return false;
}
if (spellAbilities.size() > 1) {
// if there are more abilities (fused split spell) or first ability added new abilities (splice), activate the additional abilities
boolean ignoreAbility = true;
boolean payNoMana = noMana;
for (SpellAbility spellAbility : spellAbilities) {
if (ignoreAbility) {
ignoreAbility = false;
} else {
// costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities
payNoMana |= spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE;
if (!spellAbility.activate(game, payNoMana)) {
return false;
}
}
}
}
setDoneActivatingManaAbilities(false); // can be activated again maybe during the resolution of the spell (e.g. Metallic Rebuke)
return true;
}
public String getActivatedMessage(Game game) {
StringBuilder sb = new StringBuilder();
if (isCopiedSpell()) {
sb.append(" copies ");
} else {
sb.append(" casts ");
}
return sb.append(ability.getGameLogMessage(game)).toString();
}
public String getSpellCastText(Game game) {
for (Ability spellAbility : getAbilities()) {
if (spellAbility instanceof MorphAbility
&& ((AlternativeSourceCosts) spellAbility).isActivated(getSpellAbility(), game)) {
return "a card face down";
}
}
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString());
}
@Override
public boolean resolve(Game game) {
boolean result;
Player controller = game.getPlayer(getControllerId());
if (controller == null) {
return false;
}
if (this.isInstant() || this.isSorcery()) {
int index = 0;
result = false;
boolean legalParts = false;
boolean notTargeted = true;
// check for legal parts
for (SpellAbility spellAbility : this.spellAbilities) {
// if muliple modes are selected, and there are modes with targets, then at least one mode has to have a legal target or
// When resolving a fused split spell with multiple targets, treat it as you would any spell with multiple targets.
// If all targets are illegal when the spell tries to resolve, the spell is countered and none of its effects happen.
// If at least one target is still legal at that time, the spell resolves, but an illegal target can't perform any actions
// or have any actions performed on it.
// if only a spliced spell has targets and all targets ar illegal, the complete spell is countered
if (hasTargets(spellAbility, game)) {
notTargeted = false;
legalParts |= spellAbilityHasLegalParts(spellAbility, game);
}
}
// resolve if legal parts
if (notTargeted || legalParts) {
for (SpellAbility spellAbility : this.spellAbilities) {
if (spellAbilityHasLegalParts(spellAbility, game)) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
spellAbility.getModes().setActiveMode(modeId);
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
if (spellAbility.getSpellAbilityType() != SpellAbilityType.SPLICE) {
updateOptionalCosts(index);
}
result |= spellAbility.resolve(game);
}
}
index++;
}
}
if (game.getState().getZone(card.getMainCard().getId()) == Zone.STACK) {
if (!isCopy()) {
controller.moveCards(card, Zone.GRAVEYARD, ability, game);
}
}
return result;
}
//20091005 - 608.2b
if (!game.isSimulation()) {
game.informPlayers(getName() + " has been fizzled.");
}
counter(null, game);
return false;
} else if (this.isEnchantment() && this.getSubtype(game).contains("Aura")) {
if (ability.getTargets().stillLegal(ability, game)) {
updateOptionalCosts(0);
boolean bestow = ability instanceof BestowAbility;
if (bestow) {
// Must be removed first time, after that will be removed by continous effect
// Otherwise effects like evolve trigger from creature comes into play event
card.getCardType().remove(CardType.CREATURE);
card.getSubtype(game).add("Aura");
}
if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) {
if (bestow) {
// card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed
// TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null && permanent instanceof PermanentCard) {
permanent.setSpellAbility(ability); // otherwise spell ability without bestow will be set
card.addCardType(CardType.CREATURE);
card.getSubtype(game).remove("Aura");
}
}
return ability.resolve(game);
}
if (bestow) {
card.addCardType(CardType.CREATURE);
}
return false;
}
// Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature
if (this.getSpellAbility() instanceof BestowAbility) {
updateOptionalCosts(0);
if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) {
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null && permanent instanceof PermanentCard) {
((PermanentCard) permanent).getCard().addCardType(CardType.CREATURE);
((PermanentCard) permanent).getCard().getSubtype(game).remove("Aura");
return true;
}
}
return false;
} else {
//20091005 - 608.2b
if (!game.isSimulation()) {
game.informPlayers(getName() + " has been fizzled.");
}
counter(null, game);
return false;
}
} else {
updateOptionalCosts(0);
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
}
}
private boolean hasTargets(SpellAbility spellAbility, Game game) {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (!mode.getTargets().isEmpty()) {
return true;
}
}
return false;
} else {
return !spellAbility.getTargets().isEmpty();
}
}
private boolean spellAbilityHasLegalParts(SpellAbility spellAbility, Game game) {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
boolean targetedMode = false;
boolean legalTargetedMode = false;
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (!mode.getTargets().isEmpty()) {
targetedMode = true;
if (mode.getTargets().stillLegal(spellAbility, game)) {
legalTargetedMode = true;
}
}
}
if (targetedMode) {
return legalTargetedMode;
}
return true;
} else {
return spellAbility.getTargets().stillLegal(spellAbility, game);
}
}
/**
* As we have ability in the stack, we need to update optional costs in
* original card. This information will be used later by effects, e.g. to
* determine whether card was kicked or not. E.g. Desolation Angel
*/
private void updateOptionalCosts(int index) {
spellCards.get(index).getAbilities().get(spellAbilities.get(index).getId()).ifPresent(abilityOrig
-> {
for (Object object : spellAbilities.get(index).getOptionalCosts()) {
Cost cost = (Cost) object;
for (Cost costOrig : abilityOrig.getOptionalCosts()) {
if (cost.getId().equals(costOrig.getId())) {
if (cost.isPaid()) {
costOrig.setPaid();
} else {
costOrig.clearPaid();
}
break;
}
}
}
});
}
@Override
public void counter(UUID sourceId, Game game) {
this.counter(sourceId, game, Zone.GRAVEYARD, false, ZoneDetail.NONE);
}
@Override
public void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
this.countered = true;
if (!isCopiedSpell()) {
Player player = game.getPlayer(game.getControllerId(sourceId));
if (player == null) {
player = game.getPlayer(getControllerId());
}
if (player != null) {
Ability counteringAbility = null;
MageObject counteringObject = game.getObject(sourceId);
if (counteringObject instanceof StackObject) {
counteringAbility = ((StackObject) counteringObject).getStackAbility();
}
if (zone == Zone.LIBRARY) {
if (zoneDetail == ZoneDetail.CHOOSE) {
if (player.chooseUse(Outcome.Detriment, "Move countered spell to the top of the library? (otherwise it goes to the bottom)", counteringAbility, game)) {
zoneDetail = ZoneDetail.TOP;
} else {
zoneDetail = ZoneDetail.BOTTOM;
}
}
if (zoneDetail == ZoneDetail.TOP) {
player.putCardsOnTopOfLibrary(new CardsImpl(card), game, counteringAbility, false);
} else {
player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, counteringAbility, false);
}
} else {
player.moveCards(card, zone, counteringAbility, game, false, false, owner, null);
}
}
} else {
// Copied spell, only remove from stack
game.getStack().remove(this);
}
}
public boolean isDoneActivatingManaAbilities() {
return doneActivatingManaAbilities;
}
public void setDoneActivatingManaAbilities(boolean doneActivatingManaAbilities) {
this.doneActivatingManaAbilities = doneActivatingManaAbilities;
}
@Override
public UUID getSourceId() {
return card.getId();
}
@Override
public UUID getControllerId() {
return this.controllerId;
}
@Override
public String getName() {
return card.getName();
}
@Override
public String getIdName() {
String idName;
if (card != null) {
idName = card.getId().toString().substring(0, 3);
} else {
idName = getId().toString().substring(0, 3);
}
return getName() + " [" + idName + ']';
}
@Override
public String getLogName() {
if (faceDown) {
if (this.isCreature()) {
return "face down creature spell";
} else {
return "face down spell";
}
}
return GameLog.getColoredObjectIdName(card);
}
@Override
public String getImageName() {
return card.getImageName();
}
@Override
public void setName(String name) {
}
@Override
public Rarity getRarity() {
return card.getRarity();
}
@Override
public EnumSet<CardType> getCardType() {
if (faceDown) {
EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
cardTypes.add(CardType.CREATURE);
return cardTypes;
}
if (this.getSpellAbility() instanceof BestowAbility) {
EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
cardTypes.addAll(card.getCardType());
cardTypes.remove(CardType.CREATURE);
return cardTypes;
}
return card.getCardType();
}
@Override
public List<String> getSubtype(Game game) {
if (this.getSpellAbility() instanceof BestowAbility) {
List<String> subtypes = new ArrayList<>();
subtypes.addAll(card.getSubtype(game));
subtypes.add("Aura");
return subtypes;
}
return card.getSubtype(game);
}
@Override
public boolean hasSubtype(String subtype, Game game) {
if (this.getSpellAbility() instanceof BestowAbility) { // workaround for Bestow (don't like it)
List<String> subtypes = new ArrayList<>();
subtypes.addAll(card.getSubtype(game));
subtypes.add("Aura");
if (subtypes.contains(subtype)) {
return true;
}
}
return card.hasSubtype(subtype, game);
}
@Override
public EnumSet<SuperType> getSuperType() {
return card.getSuperType();
}
public List<SpellAbility> getSpellAbilities() {
return spellAbilities;
}
@Override
public Abilities<Ability> getAbilities() {
return card.getAbilities();
}
@Override
public Abilities<Ability> getAbilities(Game game) {
return card.getAbilities(game);
}
@Override
public boolean hasAbility(UUID abilityId, Game game) {
return card.hasAbility(abilityId, game);
}
@Override
public ObjectColor getColor(Game game) {
return color;
}
@Override
public ObjectColor getFrameColor(Game game) {
return frameColor;
}
@Override
public FrameStyle getFrameStyle() {
return frameStyle;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return card.getManaCost();
}
/**
* 202.3b When calculating the converted mana cost of an object with an {X}
* in its mana cost, X is treated as 0 while the object is not on the stack,
* and X is treated as the number chosen for it while the object is on the
* stack.
*
* @return
*/
@Override
public int getConvertedManaCost() {
int cmc = 0;
if (faceDown) {
return 0;
}
for (SpellAbility spellAbility : spellAbilities) {
cmc += spellAbility.getConvertedXManaCost(getCard());
}
cmc += getCard().getManaCost().convertedManaCost();
return cmc;
}
@Override
public MageInt getPower() {
return card.getPower();
}
@Override
public MageInt getToughness() {
return card.getToughness();
}
@Override
public int getStartingLoyalty() {
return card.getStartingLoyalty();
}
@Override
public UUID getId() {
return id;
}
@Override
public UUID getOwnerId() {
return card.getOwnerId();
}
public void addSpellAbility(SpellAbility spellAbility) {
spellAbilities.add(spellAbility);
}
public void addAbility(Ability ability) {
}
@Override
public SpellAbility getSpellAbility() {
return ability;
}
public void setControllerId(UUID controllerId) {
this.ability.setControllerId(controllerId);
for (SpellAbility spellAbility : spellAbilities) {
spellAbility.setControllerId(controllerId);
}
this.controllerId = controllerId;
}
@Override
public void setOwnerId(UUID controllerId) {
}
@Override
public List<String> getRules() {
return card.getRules();
}
@Override
public List<String> getRules(Game game) {
return card.getRules(game);
}
@Override
public String getExpansionSetCode() {
return card.getExpansionSetCode();
}
@Override
public String getTokenSetCode() {
return card.getTokenSetCode();
}
@Override
public String getTokenDescriptor() {
return card.getTokenDescriptor();
}
@Override
public void setFaceDown(boolean value, Game game) {
faceDown = value;
}
@Override
public boolean turnFaceUp(Game game, UUID playerId) {
setFaceDown(false, game);
return true;
}
@Override
public boolean turnFaceDown(Game game, UUID playerId) {
setFaceDown(true, game);
return true;
}
@Override
public boolean isFaceDown(Game game) {
return faceDown;
}
@Override
public boolean isFlipCard() {
return false;
}
@Override
public String getFlipCardName() {
return null;
}
@Override
public boolean isSplitCard() {
return false;
}
@Override
public boolean isTransformable() {
return false;
}
@Override
public Card getSecondCardFace() {
return null;
}
@Override
public boolean isNightCard() {
return false;
}
@Override
public Spell copy() {
return new Spell(this);
}
public Spell copySpell(UUID newController) {
Spell copy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone);
boolean firstDone = false;
for (SpellAbility spellAbility : this.getSpellAbilities()) {
if (!firstDone) {
firstDone = true;
continue;
}
SpellAbility newAbility = spellAbility.copy(); // e.g. spliced spell
newAbility.newId();
copy.addSpellAbility(newAbility);
}
copy.setCopy(true);
copy.setControllerId(newController);
return copy;
}
@Override
public void adjustCosts(Ability ability, Game game) {
if (card != null) {
card.adjustCosts(ability, game);
}
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (card != null) {
card.adjustTargets(ability, game);
}
}
@Override
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
return card.removeFromZone(game, fromZone, sourceId);
}
@Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) {
return moveToZone(zone, sourceId, game, flag, null);
}
@Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
// 706.10a If a copy of a spell is in a zone other than the stack, it ceases to exist.
// If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist.
// These are state-based actions. See rule 704.
if (this.isCopiedSpell() && zone != Zone.STACK) {
return true;
}
return card.moveToZone(zone, sourceId, game, flag, appliedEffects);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game) {
return moveToExile(exileId, name, sourceId, game, null);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
return this.card.moveToExile(exileId, name, sourceId, game, appliedEffects);
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, ArrayList<UUID> appliedEffects) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public String getCardNumber() {
return card.getCardNumber();
}
@Override
public boolean getUsesVariousArt() {
return card.getUsesVariousArt();
}
@Override
public List<Mana> getMana() {
return card.getMana();
}
@Override
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public void setZoneChangeCounter(int value, Game game) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public Ability getStackAbility() {
return this.ability;
}
@Override
public void assignNewId() {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public void setTransformable(boolean value) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public int getZoneChangeCounter(Game game) {
return card.getZoneChangeCounter(game);
}
@Override
public void addInfo(String key, String value, Game game) {
// do nothing
}
public void setCopiedSpell(boolean isCopied) {
this.copiedSpell = isCopied;
}
public boolean isCopiedSpell() {
return this.copiedSpell;
}
public Zone getFromZone() {
return this.fromZone;
}
@Override
public void setCopy(boolean isCopy) {
setCopiedSpell(isCopy);
}
@Override
public boolean isCopy() {
return isCopiedSpell();
}
@Override
public Counters getCounters(Game game) {
return card.getCounters(game);
}
@Override
public Counters getCounters(GameState state) {
return card.getCounters(state);
}
@Override
public boolean addCounters(Counter counter, Ability source, Game game) {
return card.addCounters(counter, source, game);
}
@Override
public boolean addCounters(Counter counter, Ability source, Game game, ArrayList<UUID> appliedEffects) {
return card.addCounters(counter, source, game, appliedEffects);
}
@Override
public void removeCounters(String name, int amount, Game game) {
card.removeCounters(name, amount, game);
}
@Override
public void removeCounters(Counter counter, Game game) {
card.removeCounters(counter, game);
}
public Card getCard() {
return card;
}
@Override
public Card getMainCard() {
return card.getMainCard();
}
@Override
public void setZone(Zone zone, Game game) {
card.setZone(zone, game);
}
@Override
public void setSpellAbility(SpellAbility ability) {
throw new UnsupportedOperationException("Not supported.");
}
public boolean isCountered() {
return countered;
}
@Override
public void checkForCountersToAdd(Permanent permanent, Game game) {
card.checkForCountersToAdd(permanent, game);
}
@Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) {
Spell copy = this.copySpell(newControllerId);
game.getStack().push(copy);
if (chooseNewTargets) {
copy.chooseNewTargets(game, newControllerId);
}
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId));
return copy;
}
}