diff --git a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java index e70723d5cc..8eef9647b3 100644 --- a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java +++ b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java @@ -108,7 +108,7 @@ class ChainOfVaporEffect extends OneShotEffect { copy.setCopiedSpell(true); game.getStack().push(copy); copy.chooseNewTargets(game, source.getControllerId()); - String activateMessage = copy.getSpellAbility().getActivatedMessage(game); + String activateMessage = copy.getActivatedMessage(game); if (activateMessage.startsWith(" casts ")) { activateMessage = activateMessage.substring(6); } diff --git a/Mage/src/mage/Constants.java b/Mage/src/mage/Constants.java index f13055ae3e..21d76f621d 100644 --- a/Mage/src/mage/Constants.java +++ b/Mage/src/mage/Constants.java @@ -154,6 +154,27 @@ public final class Constants { } } + public enum SpellAbilityType { + BASE("Basic SpellAbility"), + SPLIT("Split SpellAbility"), + SPLIT_FUSED("Split SpellAbility"), + SPLIT_LEFT("LeftSplit SpellAbility"), + SPLIT_RIGHT("RightSplit SpellAbility"), + MODE("Mode SpellAbility"), + SPLICE("Spliced SpellAbility"); + + private String text; + + SpellAbilityType(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + } + public enum EffectType { ONESHOT("One Shot Effect"), diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index 36af50bde8..d0804b2230 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -40,6 +40,7 @@ import mage.abilities.keyword.FlashAbility; import mage.game.Game; import java.util.UUID; +import mage.Constants.SpellAbilityType; /** * @@ -47,23 +48,39 @@ import java.util.UUID; */ public class SpellAbility extends ActivatedAbilityImpl { + private SpellAbilityType spellAbilityType; + public SpellAbility(ManaCost cost, String cardName) { this(cost, cardName, Zone.HAND); } public SpellAbility(ManaCost cost, String cardName, Zone zone) { - super(AbilityType.SPELL, zone); - this.addManaCost(cost); - this.name = "Cast " + cardName; + this(cost, cardName, zone, SpellAbilityType.BASE); } - public SpellAbility(Cost cost, String cardName, Effect effect, Zone zone) { - super(zone, effect, cost); - this.name = "Cast " + cardName; + public SpellAbility(ManaCost cost, String cardName, Zone zone, SpellAbilityType spellAbilityType) { + super(AbilityType.SPELL, zone); + this.spellAbilityType = spellAbilityType; + this.addManaCost(cost); + switch(spellAbilityType) { + case SPLIT_FUSED: + this.name = "Cast fused " + cardName; + break; + default: + this.name = "Cast " + cardName; + } + } +// public SpellAbility(Cost cost, String cardName, Effect effect, Zone zone) { +// super(zone, effect, cost); +// this.spellAbilityType = SpellAbilityType.BASE; +// this.name = "Cast " + cardName; +// } + public SpellAbility(SpellAbility ability) { super(ability); + this.spellAbilityType = ability.spellAbilityType; } @Override @@ -124,4 +141,12 @@ public class SpellAbility extends ActivatedAbilityImpl { return spell; } + public SpellAbilityType getSpellAbilityType() { + return spellAbilityType; + } + + public void setSpellAbilityType(SpellAbilityType spellAbilityType) { + this.spellAbilityType = spellAbilityType; + } + } diff --git a/Mage/src/mage/abilities/effects/common/CopyTargetSpellEffect.java b/Mage/src/mage/abilities/effects/common/CopyTargetSpellEffect.java index 51592e3e67..5faddd9b98 100644 --- a/Mage/src/mage/abilities/effects/common/CopyTargetSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyTargetSpellEffect.java @@ -61,7 +61,7 @@ public class CopyTargetSpellEffect extends OneShotEffect game.getStack().push(copy); copy.chooseNewTargets(game, source.getControllerId()); Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getSpellAbility().getActivatedMessage(game); + String activateMessage = copy.getActivatedMessage(game); if (activateMessage.startsWith(" casts ")) { activateMessage = activateMessage.substring(6); } diff --git a/Mage/src/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/mage/abilities/keyword/FlashbackAbility.java index c07cc67f91..b33156ec7d 100644 --- a/Mage/src/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/mage/abilities/keyword/FlashbackAbility.java @@ -28,14 +28,18 @@ package mage.abilities.keyword; import mage.Constants; +import mage.Constants.SpellAbilityType; +import static mage.Constants.SpellAbilityType.SPLIT_LEFT; import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.SpellAbility; import mage.abilities.costs.Cost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.cards.Card; +import mage.cards.SplitCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -48,16 +52,22 @@ import mage.target.Target; */ public class FlashbackAbility extends /*SpellAbility*/ ActivatedAbilityImpl { + private Constants.SpellAbilityType spellAbilityType; + private String abilityName; + public FlashbackAbility(Cost cost, Constants.TimingRule timingRule) { //super(cost, "", new FlashbackEffect(), Constants.Zone.GRAVEYARD); super(Constants.Zone.GRAVEYARD, new FlashbackEffect(), cost); this.timing = timingRule; this.usesStack = false; + this.spellAbilityType = SpellAbilityType.BASE; this.addEffect(new CreateDelayedTriggeredAbilityEffect(new FlashbackTriggeredAbility())); } public FlashbackAbility(final FlashbackAbility ability) { super(ability); + this.spellAbilityType = ability.spellAbilityType; + this.abilityName = ability.abilityName; } @Override @@ -65,11 +75,16 @@ public class FlashbackAbility extends /*SpellAbility*/ ActivatedAbilityImpl 0) { - sbRule.append("--"); + sbRule.append(" - "); } else { sbRule.append(" "); } @@ -80,9 +95,26 @@ public class FlashbackAbility extends /*SpellAbility*/ ActivatedAbilityImpl(You may cast this card from your graveyard for its flashback cost. Then exile it.)"); return sbRule.toString(); } + + public void setSpellAbilityType(SpellAbilityType spellAbilityType) { + this.spellAbilityType = spellAbilityType; + } + + public SpellAbilityType getSpellAbilityType() { + return this.spellAbilityType; + } + + public void setAbilityName(String abilityName) { + this.abilityName = abilityName; + } + } class FlashbackEffect extends OneShotEffect { @@ -107,13 +139,25 @@ class FlashbackEffect extends OneShotEffect { if (card != null) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - card.getSpellAbility().clear(); + SpellAbility spellAbility; + switch(((FlashbackAbility) source).getSpellAbilityType()) { + case SPLIT_LEFT: + spellAbility = ((SplitCard)card).getLeftHalfCard().getSpellAbility(); + break; + case SPLIT_RIGHT: + spellAbility = ((SplitCard)card).getRightHalfCard().getSpellAbility(); + break; + default: + spellAbility = card.getSpellAbility(); + } + + spellAbility.clear(); int amount = source.getManaCostsToPay().getX(); - card.getSpellAbility().getManaCostsToPay().setX(amount); - for (Target target : card.getSpellAbility().getTargets()) { + spellAbility.getManaCostsToPay().setX(amount); + for (Target target : spellAbility.getTargets()) { target.setRequired(true); } - return controller.cast(card.getSpellAbility(), game, true); + return controller.cast(spellAbility, game, true); } } return false; diff --git a/Mage/src/mage/abilities/keyword/FuseAbility.java b/Mage/src/mage/abilities/keyword/FuseAbility.java deleted file mode 100644 index 98bfc5d4c1..0000000000 --- a/Mage/src/mage/abilities/keyword/FuseAbility.java +++ /dev/null @@ -1,66 +0,0 @@ -/* -* 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 mage.Constants.Zone; -import mage.abilities.SpellAbility; -import mage.abilities.costs.mana.ManaCosts; -import mage.cards.Card; - -/** - * - * @author LevelX2 - */ - -public class FuseAbility extends SpellAbility { - - public FuseAbility(Card card, ManaCosts costs) { - super(costs, "fused " + card.getName(), Zone.HAND); - } - - public FuseAbility(final FuseAbility ability) { - super(ability); - } - - @Override - public FuseAbility copy() { - return new FuseAbility(this); - } - - @Override - public String getRule(boolean all) { - return getRule(); - } - - @Override - public String getRule() { - return "Fuse (You may cast one or both halves of this card from your hand.)"; - } - -} diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index 859b78c749..6dad6a9dfa 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -49,6 +49,7 @@ import org.apache.log4j.Logger; import java.lang.reflect.Constructor; import java.util.*; +import mage.Constants.SpellAbilityType; public abstract class CardImpl> extends MageObjectImpl implements Card { @@ -75,6 +76,10 @@ public abstract class CardImpl> extends MageObjectImpl protected boolean splitCard; public CardImpl(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { + this(ownerId, cardNumber, name, rarity, cardTypes, costs, SpellAbilityType.BASE); + } + + public CardImpl(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs, SpellAbilityType spellAbilityType) { this(ownerId, name); this.rarity = rarity; this.cardNumber = cardNumber; @@ -84,7 +89,7 @@ public abstract class CardImpl> extends MageObjectImpl addAbility(new PlayLandAbility(name)); } else { - addAbility(new SpellAbility(manaCost, name)); + addAbility(new SpellAbility(manaCost, name, Zone.HAND, spellAbilityType)); } this.usesVariousArt = Character.isDigit(this.getClass().getName().charAt(this.getClass().getName().length()-1)); this.counters = new Counters(); diff --git a/Mage/src/mage/cards/SplitCard.java b/Mage/src/mage/cards/SplitCard.java index 48cc68801e..dcff7520ce 100644 --- a/Mage/src/mage/cards/SplitCard.java +++ b/Mage/src/mage/cards/SplitCard.java @@ -31,16 +31,13 @@ package mage.cards; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.Constants; import mage.Constants.CardType; import mage.Constants.Rarity; -import mage.Constants.Zone; +import mage.Constants.SpellAbilityType; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.keyword.FuseAbility; -import mage.game.Game; import mage.watchers.Watcher; /** @@ -50,16 +47,11 @@ import mage.watchers.Watcher; public abstract class SplitCard> extends CardImpl { - public enum ActiveCardHalf { - NONE, LEFT, RIGHT, BOTH - } private Card leftHalfCard; private Card rightHalfCard; - private ActiveCardHalf activeCardHalf = ActiveCardHalf.NONE; - - public SplitCard(UUID ownerId, int cardNumber, String nameLeft, String nameRight, Rarity rarity, CardType[] cardTypes, String costsLeft, String costsRight) { - super(ownerId, cardNumber, new StringBuilder(nameLeft).append(" - ").append(nameRight).toString(), rarity, cardTypes, costsLeft + costsRight); + public SplitCard(UUID ownerId, int cardNumber, String nameLeft, String nameRight, Rarity rarity, CardType[] cardTypes, String costsLeft, String costsRight, boolean fused) { + super(ownerId, cardNumber, new StringBuilder(nameLeft).append(" - ").append(nameRight).toString(), rarity, cardTypes, costsLeft + costsRight, (fused ?SpellAbilityType.SPLIT_FUSED:SpellAbilityType.SPLIT)); this.createLeftHalfCard(nameLeft, costsLeft); this.createRightHalfCard(nameRight, costsRight); this.splitCard = true; @@ -69,7 +61,6 @@ public abstract class SplitCard> extends CardImpl { super(card); this.leftHalfCard = card.leftHalfCard.copy(); this.rightHalfCard = card.rightHalfCard.copy(); - this.activeCardHalf = card.activeCardHalf; } private Card createLeftHalfCard (String nameLeft, String costsLeft) { @@ -83,7 +74,7 @@ public abstract class SplitCard> extends CardImpl { private Card createRightHalfCard (String nameRight, String costsRight) { CardType[] cardTypes = new CardType[getCardType().size()]; this.getCardType().toArray(cardTypes); - rightHalfCard = new LeftHalfCard(this.getOwnerId(), this.getCardNumber(), nameRight, this.rarity, cardTypes, costsRight); + rightHalfCard = new RightHalfCard(this.getOwnerId(), this.getCardNumber(), nameRight, this.rarity, cardTypes, costsRight); rightHalfCard.getAbilities().setSourceId(objectId); return rightHalfCard; } @@ -98,96 +89,28 @@ public abstract class SplitCard> extends CardImpl { return rightHalfCard; } - public ActiveCardHalf getActiveCardHalf() { - return activeCardHalf; - } - - @Override - public boolean cast(Game game, Constants.Zone fromZone, SpellAbility ability, UUID controllerId) { - if (this.getAbilities().contains(ability)) { - activeCardHalf = ActiveCardHalf.BOTH; - } else if (leftHalfCard.getAbilities().contains(ability)) { - activeCardHalf = ActiveCardHalf.LEFT; - } else if (rightHalfCard.getAbilities().contains(ability)) { - activeCardHalf = ActiveCardHalf.RIGHT; - } else { - activeCardHalf = ActiveCardHalf.NONE; - } - if (super.cast(game, fromZone, ability, controllerId)) { - return true; - } - activeCardHalf = ActiveCardHalf.NONE; - return false; - } - - @Override - public boolean moveToZone(Constants.Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList appliedEffects) { - if (super.moveToZone(toZone, sourceId, game, flag, appliedEffects)) { - if (!toZone.equals(Zone.STACK)) { - activeCardHalf = ActiveCardHalf.NONE; - } - return true; - } - return false; - } - - @Override - public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList appliedEffects) { - if(super.moveToExile(exileId, name, sourceId, game, appliedEffects)) { - activeCardHalf = ActiveCardHalf.NONE; - return true; - } - return false; - } - @Override public Abilities getAbilities(){ Abilities allAbilites = new AbilitiesImpl(); - if (activeCardHalf.equals(ActiveCardHalf.NONE) || activeCardHalf.equals(ActiveCardHalf.LEFT)) { - allAbilites.addAll(leftHalfCard.getAbilities()); - } - if (activeCardHalf.equals(ActiveCardHalf.NONE) || activeCardHalf.equals(ActiveCardHalf.RIGHT)) { - allAbilites.addAll(rightHalfCard.getAbilities()); - } - for (Ability ability: super.getAbilities()) { - if (ability instanceof FuseAbility) { + for (Ability ability : super.getAbilities()) { + if (ability instanceof SpellAbility && !((SpellAbility)ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT)) { allAbilites.add(ability); } } + allAbilites.addAll(super.getAbilities()); + allAbilites.addAll(leftHalfCard.getAbilities()); + allAbilites.addAll(rightHalfCard.getAbilities()); return allAbilites; } - @Override - public SpellAbility getSpellAbility() { - switch (activeCardHalf) { - case LEFT: - return leftHalfCard.getSpellAbility(); - case RIGHT: - return rightHalfCard.getSpellAbility(); - } - return null; - } - @Override public List getRules() { List rules = new ArrayList(); - if (activeCardHalf.equals(ActiveCardHalf.NONE) || activeCardHalf.equals(ActiveCardHalf.LEFT)) { - rules.add(new StringBuilder("").append(leftHalfCard.getName()).append("").toString()); - rules.addAll(leftHalfCard.getRules()); - } - if (activeCardHalf.equals(ActiveCardHalf.NONE)) { - rules.add("
"); - } - if (activeCardHalf.equals(ActiveCardHalf.NONE) || activeCardHalf.equals(ActiveCardHalf.RIGHT)) { - rules.add(new StringBuilder("").append(rightHalfCard.getName()).append("").toString()); - rules.addAll(rightHalfCard.getRules()); - } - - for (Ability ability: super.getAbilities()) { - if (ability instanceof FuseAbility) { - rules.add("
------------------------------------------------------------"); - rules.add(ability.getRule()); - } + rules.addAll(leftHalfCard.getRules()); + rules.addAll(rightHalfCard.getRules()); + if (getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { + rules.add("------------------------------------------------------------------------"); + rules.add("Fuse (You may cast one or both halves of this card from your hand.)"); } return rules; } @@ -213,14 +136,9 @@ public abstract class SplitCard> extends CardImpl { @Override public List getWatchers() { List allWatchers = new ArrayList(); - switch (activeCardHalf) { - case LEFT: - allWatchers.addAll(leftHalfCard.getWatchers()); - break; - case RIGHT: - allWatchers.addAll(rightHalfCard.getWatchers()); - break; - } + allWatchers.addAll(super.getWatchers()); + allWatchers.addAll(leftHalfCard.getWatchers()); + allWatchers.addAll(rightHalfCard.getWatchers()); return allWatchers; } } @@ -230,8 +148,8 @@ public abstract class SplitCard> extends CardImpl { */ class LeftHalfCard extends CardImpl { - public LeftHalfCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { - super(ownerId, cardNumber, name, rarity, cardTypes, costs); + public LeftHalfCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs ) { + super(ownerId, cardNumber, name, rarity, cardTypes, costs, SpellAbilityType.SPLIT_LEFT); } public LeftHalfCard(final LeftHalfCard card) { @@ -242,6 +160,16 @@ class LeftHalfCard extends CardImpl { public LeftHalfCard copy() { return new LeftHalfCard(this); } + + @Override + public List getRules() { + List rules = new ArrayList(); + rules.add(new StringBuilder("").append(this.getName()).append("").toString()); + rules.addAll(super.getRules()); + return rules; + } + + } /* @@ -250,7 +178,7 @@ class LeftHalfCard extends CardImpl { class RightHalfCard extends CardImpl { public RightHalfCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) { - super(ownerId, cardNumber, name, rarity, cardTypes, costs); + super(ownerId, cardNumber, name, rarity, cardTypes, costs, SpellAbilityType.SPLIT_RIGHT); } public RightHalfCard(final RightHalfCard card) { @@ -261,4 +189,12 @@ class RightHalfCard extends CardImpl { public RightHalfCard copy() { return new RightHalfCard(this); } + + @Override + public List getRules() { + List rules = new ArrayList(); + rules.add(new StringBuilder("").append(this.getName()).append("").toString()); + rules.addAll(super.getRules()); + return rules; + } } diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 9fb9c70015..fed196e626 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -37,6 +37,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.Effect; import mage.cards.Card; +import mage.cards.SplitCard; import mage.choices.Choice; import mage.game.combat.Combat; import mage.game.combat.CombatGroup; @@ -454,6 +455,10 @@ public class GameState implements Serializable, Copyable { for (Ability ability: card.getAbilities()) { addAbility(ability, card); } + if (card.isSplitCard()) { + addCard( ((SplitCard)card).getLeftHalfCard()); + addCard( ((SplitCard)card).getRightHalfCard()); + } } @Deprecated diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index d0cdc8cc8a..72e27ea344 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -55,6 +55,10 @@ import mage.watchers.Watcher; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.Constants; +import static mage.Constants.SpellAbilityType.SPLIT_LEFT; +import static mage.Constants.SpellAbilityType.SPLIT_RIGHT; +import mage.cards.SplitCard; /** * @@ -62,35 +66,87 @@ import java.util.UUID; */ public class Spell> implements StackObject, Card { + private List spellCards = new ArrayList(); + private List spellAbilities = new ArrayList(); + private Card card; private SpellAbility ability; private UUID controllerId; private boolean copiedSpell; private Zone fromZone; + private UUID id; public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) { this.card = card; + id = ability.getId(); this.ability = ability; this.ability.setControllerId(controllerId); + if (ability.getSpellAbilityType().equals(Constants.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; } public Spell(final Spell spell) { - this.card = spell.card.copy(); - this.ability = spell.ability.copy(); + 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 = 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; } + + public boolean activate(Game game, boolean noMana) { + for (SpellAbility spellAbility: spellAbilities) { + if (!spellAbility.activate(game, noMana)) { + return false; + } + } + return true; + } + + public String getActivatedMessage(Game game) { + return ability.getActivatedMessage(game); + } + @Override public boolean resolve(Game game) { boolean result; if (card.getCardType().contains(CardType.INSTANT) || card.getCardType().contains(CardType.SORCERY)) { - if (ability.getTargets().stillLegal(ability, game)) { - updateOptionalCosts(); - result = ability.resolve(game); + int index = 0; + result = false; + boolean legalParts = false; + for(SpellAbility spellAbility: this.spellAbilities) { + if (spellAbility.getTargets().stillLegal(ability, game)) { + legalParts = true; + updateOptionalCosts(index); + result |= spellAbility.resolve(game); + } + index++; + } + if (legalParts) { if (!copiedSpell) { for (Effect effect : ability.getEffects()) { if (effect instanceof PostResolveEffect) { @@ -102,7 +158,6 @@ public class Spell> implements StackObject, Card { card.moveToZone(Zone.GRAVEYARD, ability.getId(), game, false); } } - return result; } //20091005 - 608.2b @@ -111,7 +166,7 @@ public class Spell> implements StackObject, Card { return false; } else if (card.getCardType().contains(CardType.ENCHANTMENT) && card.getSubtype().contains("Aura")) { if (ability.getTargets().stillLegal(ability, game)) { - updateOptionalCosts(); + updateOptionalCosts(0); if (card.putOntoBattlefield(game, Zone.HAND, ability.getId(), controllerId)) { return ability.resolve(game); } @@ -122,7 +177,7 @@ public class Spell> implements StackObject, Card { counter(null, game); return false; } else { - updateOptionalCosts(); + updateOptionalCosts(0); result = card.putOntoBattlefield(game, Zone.HAND, ability.getId(), controllerId); return result; } @@ -133,10 +188,10 @@ public class Spell> implements StackObject, 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() { - Ability abilityOrig = card.getAbilities().get(ability.getId()); + private void updateOptionalCosts(int index) { + Ability abilityOrig = spellCards.get(index).getAbilities().get(spellAbilities.get(index).getId()); if (abilityOrig != null) { - for (Object object : ability.getOptionalCosts()) { + for (Object object : spellAbilities.get(index).getOptionalCosts()) { Cost cost = (Cost) object; for (Cost costOrig : abilityOrig.getOptionalCosts()) { if (cost.getId().equals(costOrig.getId())) { @@ -163,29 +218,31 @@ public class Spell> implements StackObject, Card { public boolean chooseNewTargets(Game game, UUID playerId) { Player player = game.getPlayer(playerId); if (player != null) { - for (Target target: ability.getTargets()) { - Target newTarget = target.copy(); - newTarget.clearChosen(); - for (UUID targetId: target.getTargets()) { - MageObject object = game.getObject(targetId); - String name = null; - if (object == null) { - Player targetPlayer = game.getPlayer(targetId); - if (targetPlayer != null) name = targetPlayer.getName(); - } else { - name = object.getName(); + for(SpellAbility spellAbility: spellAbilities) { + for (Target target: spellAbility.getTargets()) { + Target newTarget = target.copy(); + newTarget.clearChosen(); + for (UUID targetId: target.getTargets()) { + MageObject object = game.getObject(targetId); + String name = null; + if (object == null) { + Player targetPlayer = game.getPlayer(targetId); + if (targetPlayer != null) name = targetPlayer.getName(); + } else { + name = object.getName(); + } + if (name != null && player.chooseUse(spellAbility.getEffects().get(0).getOutcome(), "Change target from " + name + "?", game)) { + if (!player.chooseTarget(spellAbility.getEffects().get(0).getOutcome(), newTarget, spellAbility, game)) + newTarget.addTarget(targetId, spellAbility, game, false); + } + else { + newTarget.addTarget(targetId, spellAbility, game, false); + } } - if (name != null && player.chooseUse(ability.getEffects().get(0).getOutcome(), "Change target from " + name + "?", game)) { - if (!player.chooseTarget(ability.getEffects().get(0).getOutcome(), newTarget, ability, game)) - newTarget.addTarget(targetId, ability, game, false); + target.clearChosen(); + for (UUID newTargetId: newTarget.getTargets()) { + target.addTarget(newTargetId, spellAbility, game, false); } - else { - newTarget.addTarget(targetId, ability, game, false); - } - } - target.clearChosen(); - for (UUID newTargetId: newTarget.getTargets()) { - target.addTarget(newTargetId, ability, game, false); } } return true; @@ -271,7 +328,7 @@ public class Spell> implements StackObject, Card { @Override public UUID getId() { - return ability.getId(); + return id; } @Override @@ -293,6 +350,9 @@ public class Spell> implements StackObject, Card { @Override public void setControllerId(UUID controllerId) { this.ability.setControllerId(controllerId); + for (SpellAbility spellAbility: spellAbilities) { + spellAbility.setControllerId(controllerId); + } this.controllerId = controllerId; } @@ -301,12 +361,26 @@ public class Spell> implements StackObject, Card { @Override public List getRules() { - return card.getRules(); + switch (ability.getSpellAbilityType()) { + case SPLIT_LEFT: + return ((SplitCard)card).getLeftHalfCard().getRules(); + case SPLIT_RIGHT: + return ((SplitCard)card).getRightHalfCard().getRules(); + default: + return card.getRules(); + } } @Override public List getWatchers() { - return card.getWatchers(); + switch (ability.getSpellAbilityType()) { + case SPLIT_LEFT: + return ((SplitCard)card).getLeftHalfCard().getWatchers(); + case SPLIT_RIGHT: + return ((SplitCard)card).getLeftHalfCard().getWatchers(); + default: + return card.getWatchers(); + } } @Override diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 675ba3de97..81be3063d6 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -73,6 +73,10 @@ import org.apache.log4j.Logger; import java.io.Serializable; import java.util.*; +import mage.Constants; +import mage.Constants.SpellAbilityType; +import mage.cards.SplitCard; +import mage.game.stack.Spell; public abstract class PlayerImpl> implements Player, Serializable { @@ -598,13 +602,12 @@ public abstract class PlayerImpl> implements Player, Ser int bookmark = game.bookmarkState(); Zone fromZone = game.getState().getZone(card.getId()); card.cast(game, fromZone, ability, playerId); - - SpellAbility spellAbility = game.getStack().getSpell(ability.getId()).getSpellAbility(); - if (spellAbility.activate(game, noMana)) { - GameEvent event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spellAbility.getId(), spellAbility.getSourceId(), playerId); + Spell spell = game.getStack().getSpell(ability.getId()); + if (spell.activate(game, noMana)) { + GameEvent event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId); event.setZone(fromZone); game.fireEvent(event); - game.fireInformEvent(name + spellAbility.getActivatedMessage(game)); + game.fireInformEvent(new StringBuilder(name).append(spell.getActivatedMessage(game)).toString()); game.removeBookmark(bookmark); resetStoredBookmark(game); return true; @@ -775,7 +778,7 @@ public abstract class PlayerImpl> implements Player, Ser protected LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap(); for (ActivatedAbility ability: object.getAbilities().getActivatedAbilities(zone)) { - + if (ability.canActivate(playerId, game)) { useable.put(ability.getId(), ability); } @@ -785,7 +788,7 @@ public abstract class PlayerImpl> implements Player, Ser for (ActivatedAbility ability: object.getAbilities().getActivatedAbilities(Zone.HAND)) { useable.put(ability.getId(), ability); } - } + } //Alternative cost are not use for Flashback. This lines alloews to play spell with Alternative cost (like Force of Will) from wrong zone /*else { // this allows alternative costs like Flashback work from other than hand zones @@ -801,11 +804,39 @@ public abstract class PlayerImpl> implements Player, Ser Abilities otherAbilities = game.getState().getOtherAbilities(object.getId(), zone); if (otherAbilities != null) { for (ActivatedAbility ability: otherAbilities) { - useable.put(ability.getId(), ability); + Card card = game.getCard(ability.getSourceId()); + if (card.isSplitCard() && ability instanceof FlashbackAbility) { + FlashbackAbility flashbackAbility; + if (card.getCardType().contains(Constants.CardType.INSTANT)) { + flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), Constants.TimingRule.INSTANT); + } + else { + flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), Constants.TimingRule.SORCERY); + } + flashbackAbility.setSourceId(card.getId()); + flashbackAbility.setControllerId(card.getOwnerId()); + flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT); + flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName()); + useable.put(flashbackAbility.getId(), flashbackAbility); + if (card.getCardType().contains(Constants.CardType.INSTANT)) { + flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), Constants.TimingRule.INSTANT); + } + else { + flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), Constants.TimingRule.SORCERY); + } + flashbackAbility.setSourceId(card.getId()); + flashbackAbility.setControllerId(card.getOwnerId()); + flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT); + flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName()); + useable.put(flashbackAbility.getId(), flashbackAbility); + + } else { + useable.put(ability.getId(), ability); + } } } return useable; - } + } protected LinkedHashMap getUseableManaAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap();