From 725ee6e04222ade99052df9b2dfb1c7c5024ea6e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 26 Jun 2013 17:34:14 +0200 Subject: [PATCH] Added SpliceOntoArcane Ability. It's working but it will need some handling improvements. --- Mage/src/mage/abilities/AbilityImpl.java | 19 +- .../abilities/effects/ContinuousEffects.java | 60 +++++ .../abilities/effects/SpliceCardEffect.java | 66 ++++++ .../effects/SpliceCardEffectImpl.java | 67 ++++++ .../continious/GainAbilityTargetEffect.java | 36 ++- .../keyword/SpliceOntoArcaneAbility.java | 210 ++++++++++++++++++ Mage/src/mage/constants/EffectType.java | 3 +- Mage/src/mage/game/stack/Spell.java | 24 +- 8 files changed, 470 insertions(+), 15 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/SpliceCardEffect.java create mode 100644 Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java create mode 100644 Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index be15e14b18c..7d760b55b0b 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.abilities.keyword.EntwineAbility; +import mage.constants.SpellAbilityType; /** @@ -162,11 +163,22 @@ public abstract class AbilityImpl> implements Ability { @Override public boolean activate(Game game, boolean noMana) { - // 20110204 - 700.2 + /* 20130201 - 601.2b + * If the spell is modal the player announces the mode choice (see rule 700.2). + */ if (!modes.choose(game, this)) { return false; } - //20100716 - 601.2b + + /* 20130201 - 601.2b + * If the player wishes to splice any cards onto the spell (see rule 702.45), he + * or she reveals those cards in his or her hand. + */ + if (this.abilityType.equals(AbilityType.SPELL)) { + game.getContinuousEffects().applySpliceEffects(this, game); + } + + Card card = game.getCard(sourceId); if (card != null) { card.adjustChoices(this, game); @@ -179,7 +191,7 @@ public abstract class AbilityImpl> implements Ability { } } - // 20121001 - 601.2b + // 20130201 - 601.2b // If the spell has alternative or additional costs that will be paid as it's being cast such // as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his // or her intentions to pay any or all of those costs (see rule 601.2e). @@ -198,6 +210,7 @@ public abstract class AbilityImpl> implements Ability { } } } + // 20121001 - 601.2b // If the spell has a variable cost that will be paid as it's being cast (such as an {X} in // its mana cost; see rule 107.3), the player announces the value of that variable. diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index b08a3fa92a0..6919f6ca31b 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -36,7 +36,9 @@ import mage.constants.Layer; import mage.constants.SubLayer; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; +import mage.constants.SpellAbilityType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -58,6 +60,7 @@ public class ContinuousEffects implements Serializable { private ContinuousEffectsList restrictionEffects = new ContinuousEffectsList(); private ContinuousEffectsList asThoughEffects = new ContinuousEffectsList(); private ContinuousEffectsList costModificationEffects = new ContinuousEffectsList(); + private ContinuousEffectsList spliceCardEffects = new ContinuousEffectsList(); private List> allEffectsLists = new ArrayList>(); @@ -88,6 +91,7 @@ public class ContinuousEffects implements Serializable { restrictionEffects = effect.restrictionEffects.copy(); asThoughEffects = effect.asThoughEffects.copy(); costModificationEffects = effect.costModificationEffects.copy(); + spliceCardEffects = effect.spliceCardEffects.copy(); for (Map.Entry entry : effect.sources.entrySet()) { sources.put(entry.getKey(), entry.getValue()); } @@ -103,6 +107,7 @@ public class ContinuousEffects implements Serializable { allEffectsLists.add(restrictionEffects); allEffectsLists.add(asThoughEffects); allEffectsLists.add(costModificationEffects); + allEffectsLists.add(spliceCardEffects); } public ContinuousEffects copy() { @@ -125,6 +130,7 @@ public class ContinuousEffects implements Serializable { restrictionEffects.removeEndOfCombatEffects(); asThoughEffects.removeEndOfCombatEffects(); costModificationEffects.removeEndOfCombatEffects(); + spliceCardEffects.removeEndOfCombatEffects(); } public void removeEndOfTurnEffects() { @@ -135,6 +141,7 @@ public class ContinuousEffects implements Serializable { restrictionEffects.removeEndOfTurnEffects(); asThoughEffects.removeEndOfTurnEffects(); costModificationEffects.removeEndOfTurnEffects(); + spliceCardEffects.removeEndOfTurnEffects(); } public void removeInactiveEffects(Game game) { @@ -145,6 +152,7 @@ public class ContinuousEffects implements Serializable { restrictionEffects.removeInactiveEffects(game); asThoughEffects.removeInactiveEffects(game); costModificationEffects.removeInactiveEffects(game); + spliceCardEffects.removeInactiveEffects(game); } public List getLayeredEffects(Game game) { @@ -328,6 +336,29 @@ public class ContinuousEffects implements Serializable { return costEffects; } + /** + * Filters out splice effects that are not active. + * + * @param game + * @return + */ + private List getApplicableSpliceCardEffects(Game game) { + List spliceEffects = new ArrayList(); + + for (SpliceCardEffect effect: spliceCardEffects) { + HashSet abilities = spliceCardEffects.getAbility(effect.getId()); + for (Ability ability : abilities) { + if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { + spliceEffects.add(effect); + break; + } + } + } + } + + return spliceEffects; + } public boolean asThough(UUID objectId, AsThoughEffectType type, Game game) { List asThoughEffectsList = getApplicableAsThoughEffects(game); @@ -390,6 +421,31 @@ public class ContinuousEffects implements Serializable { } } + /** + * Checks all available splice effects to be applied. + * + * @param abilityToModify + * @param game + * @return + */ + public void applySpliceEffects ( Ability abilityToModify, Game game ) { + if ( ((SpellAbility) abilityToModify).getSpellAbilityType().equals(SpellAbilityType.SPLICE)) { + // on a spliced ability of a spell can't be spliced again + return; + } + List spliceEffects = getApplicableSpliceCardEffects(game); + + for ( SpliceCardEffect effect : spliceEffects) { + HashSet abilities = spliceCardEffects.getAbility(effect.getId()); + for (Ability ability : abilities) { + if ( effect.applies(abilityToModify, ability, game) ) { + effect.apply(game, ability, abilityToModify); + } + } + } + } + + public boolean replaceEvent(GameEvent event, Game game) { boolean caught = false; HashMap> consumed = new HashMap>(); @@ -617,6 +673,10 @@ public class ContinuousEffects implements Serializable { CostModificationEffect newCostModificationEffect = (CostModificationEffect)effect; costModificationEffects.addEffect(newCostModificationEffect, source); break; + case SPLICE: + SpliceCardEffect newSpliceCardEffect = (SpliceCardEffect)effect; + spliceCardEffects.addEffect(newSpliceCardEffect, source); + break; default: ContinuousEffect newEffect = (ContinuousEffect)effect; layeredEffects.addEffect(newEffect, source); diff --git a/Mage/src/mage/abilities/effects/SpliceCardEffect.java b/Mage/src/mage/abilities/effects/SpliceCardEffect.java new file mode 100644 index 00000000000..094b3a98916 --- /dev/null +++ b/Mage/src/mage/abilities/effects/SpliceCardEffect.java @@ -0,0 +1,66 @@ +/* + * 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.effects; + +import mage.abilities.Ability; +import mage.game.Game; + +/** + * Represents a {@link ContinuousEffect} that will modify the cost and abilities of a spell + * on the stack (splice a card). {@link mage.abilities.Ability Abilities} with this type of effect will be + * called once and only once from the {@link mage.abilities.AbilityImpl#activate(mage.game.Game, + * boolean) Ability.activate} method before alternative or additional costs are paid. + * + * @param + * @author levelX2 + */ + +public interface SpliceCardEffect> extends ContinuousEffect { + /** + * Called by the {@link ContinuousEffects#costModification(Ability abilityToModify, Game game) ContinuousEffects.costModification} + * method. + * + * @param game The game for which this effect should be applied. + * @param source The source ability of this effect. + * @param abilityToModify The {@link mage.abilities.SpellAbility} or {@link Ability} which should be modified. + * @return + */ + boolean apply ( Game game, Ability source, Ability abilityToModify ); + + /** + * Called by the {@link ContinuousEffects#costModification(mage.abilities.Ability, mage.game.Game) ContinuousEffects.costModification} + * method. + * + * @param abilityToModify The ability to possibly modify. + * @param source The source ability of this effect. + * @param game The game for which this effect shoul dbe applied. + * @return + */ + boolean applies(Ability abilityToModify, Ability source, Game game); +} diff --git a/Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java b/Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java new file mode 100644 index 00000000000..4a4aa90cd2f --- /dev/null +++ b/Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java @@ -0,0 +1,67 @@ +/* + * 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.effects; + +import mage.abilities.Ability; +import mage.constants.Duration; +import mage.constants.EffectType; +import mage.constants.Outcome; +import mage.game.Game; + + +/** + * Simple implementation of a {@link SpliceCardEffect} offering simplified + * construction to setup the object for use by the mage framework. + + * @author LevelX2 + */ +public abstract class SpliceCardEffectImpl> extends ContinuousEffectImpl implements SpliceCardEffect { + + public SpliceCardEffectImpl ( Duration duration, Outcome outcome ) { + super(duration, outcome); + this.effectType = EffectType.SPLICE; + } + + public SpliceCardEffectImpl(final SpliceCardEffectImpl effect) { + super(effect); + this.effectType = effect.effectType; + } + + /** + * Overridden and 'no-op' implementation put in place. + * + * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability) + * + * @param game + * @param source + * @return + */ + @Override + public final boolean apply ( Game game, Ability source ) { return false; } +} diff --git a/Mage/src/mage/abilities/effects/common/continious/GainAbilityTargetEffect.java b/Mage/src/mage/abilities/effects/common/continious/GainAbilityTargetEffect.java index b420aa7bf1d..b331aa9af68 100644 --- a/Mage/src/mage/abilities/effects/common/continious/GainAbilityTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continious/GainAbilityTargetEffect.java @@ -40,6 +40,7 @@ import mage.game.permanent.Permanent; import mage.target.Target; import java.util.UUID; +import mage.cards.Card; /** * @@ -48,23 +49,29 @@ import java.util.UUID; public class GainAbilityTargetEffect extends ContinuousEffectImpl { protected Ability ability; + // shall a card gain the ability (otherwise permanent) + private boolean onCard; public GainAbilityTargetEffect(Ability ability, Duration duration) { - super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, - ability.getEffects().size() > 0 ? ability.getEffects().get(0).getOutcome() : Outcome.AddAbility); - this.ability = ability; + this(ability, duration, null); } public GainAbilityTargetEffect(Ability ability, Duration duration, String rule) { + this(ability, duration, rule, false); + } + + public GainAbilityTargetEffect(Ability ability, Duration duration, String rule, boolean onCard) { super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability.getEffects().size() > 0 ? ability.getEffects().get(0).getOutcome() : Outcome.AddAbility); this.ability = ability; staticText = rule; + this.onCard = onCard; } public GainAbilityTargetEffect(final GainAbilityTargetEffect effect) { super(effect); this.ability = effect.ability.copy(); + this.onCard = effect.onCard; } @Override @@ -81,11 +88,24 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl 0; diff --git a/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java b/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java new file mode 100644 index 00000000000..e58bf4bc0a0 --- /dev/null +++ b/Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java @@ -0,0 +1,210 @@ +/* + * 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.Iterator; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.SpliceCardEffectImpl; +import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SpellAbilityType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; + + +/** + * 702.45. Splice + * + * 702.45a Splice is a static ability that functions while a card is in your hand. + * "Splice onto [subtype] [cost]" means "You may reveal this card from your hand + * as you cast a [subtype] spell. If you do, copy this card's text box onto that + * spell and pay [cost] as an additional cost to cast that spell." Paying a card's + * splice cost follows the rules for paying additional costs in rules 601.2b and + * 601.2e-g. + * + * Example: Since the card with splice remains in the player's hand, it can later + * be cast normally or spliced onto another spell. It can even be discarded to pay + * a "discard a card" cost of the spell it's spliced onto. + * + * 702.45b You can't choose to use a splice ability if you can't make the required + * choices (targets, etc.) for that card's instructions. You can't splice any one + * card onto the same spell more than once. If you're splicing more than one card + * onto a spell, reveal them all at once and choose the order in which their + * instructions will be followed. The instructions on the main spell have to be + * followed first. + * + * 702.45c The spell has the characteristics of the main spell, plus the text boxes + * of each of the spliced cards. The spell doesn't gain any other characteristics + * (name, mana cost, color, supertypes, card types, subtypes, etc.) of the spliced + * cards. Text copied onto the spell that refers to a card by name refers to the spell + * on the stack, not the card from which the text was copied. + * + * Example: Glacial Ray is a red card with splice onto Arcane that reads, "Glacial + * Ray deals 2 damage to target creature or player." Suppose Glacial Ray is spliced + * onto Reach Through Mists, a blue spell. The spell is still blue, and Reach Through + * Mists deals the damage. This means that the ability can target a creature with + * protection from red and deal 2 damage to that creature. + * + * 702.45d Choose targets for the added text normally (see rule 601.2c). Note that a + * spell with one or more targets will be countered if all of its targets are illegal + * on resolution. + * + * 702.45e The spell loses any splice changes once it leaves the stack (for example, + * when it's countered, it's exiled, or it resolves). + * + * Rulings + * + * You must reveal all of the cards you intend to splice at the same time. Each individual card can only be spliced once onto a spell. + * If you have more than one card with the same name in your hand, you may splice both of them onto the spell. + * A card with a splice ability can't be spliced onto itself because the spell is on the stack (and not in your hand) when you reveal the cards you want to splice onto it. + * The target for a card that's spliced onto a spell may be the same as the target chosen for the original spell or for another spliced-on card. (A recent change to the targeting rules allows this, but most other cards are unaffected by the change.) + * If you splice a targeted card onto an untargeted spell, the entire spell will be countered if the target isn't legal when the spell resolves. + * If you splice an untargeted card onto a targeted spell, the entire spell will be countered if the target isn't legal when the spell resolves. + * A spell is countered on resolution only if *all* of its targets are illegal (or the spell is countered by an effect). + * + * @author LevelX2 + */ + + +public class SpliceOntoArcaneAbility extends SimpleStaticAbility { + + private static final String KEYWORD_TEXT = "Splice onto Arcane"; + private Costs spliceCosts = new CostsImpl(); + + public SpliceOntoArcaneAbility(String manaString) { + super(Zone.HAND, new SpliceOntoArcaneEffect()); + spliceCosts.add(new ManaCostsImpl(manaString)); + } + + public SpliceOntoArcaneAbility(Cost cost) { + super(Zone.HAND, new SpliceOntoArcaneEffect()); + spliceCosts.add(cost); + } + + public SpliceOntoArcaneAbility(final SpliceOntoArcaneAbility ability) { + super(ability); + this.spliceCosts = ability.spliceCosts.copy(); + } + + @Override + public SimpleStaticAbility copy() { + return new SpliceOntoArcaneAbility(this); + } + + public Costs getSpliceCosts() { + return spliceCosts; + } + + @Override + public String getRule() { + StringBuilder sb = new StringBuilder(); + sb.append(KEYWORD_TEXT).append(" "); + sb.append(spliceCosts.getText()); + sb.append(" (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)"); + return sb.toString(); + } +} + + +class SpliceOntoArcaneEffect extends SpliceCardEffectImpl { + + public SpliceOntoArcaneEffect() { + super(Duration.WhileOnBattlefield, Outcome.Copy); + staticText = "Splice onto Arcane"; + } + + public SpliceOntoArcaneEffect(final SpliceOntoArcaneEffect effect) { + super(effect); + } + + + + @Override + public SpliceOntoArcaneEffect copy() { + return new SpliceOntoArcaneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player controller = game.getPlayer(source.getControllerId()); + Card spliceCard = game.getCard(source.getSourceId()); + if (spliceCard != null && controller != null) { + if (controller.chooseUse(outcome, new StringBuilder("Splice ").append(spliceCard.getName()) + .append(" ").append(((SpliceOntoArcaneAbility) source).getSpliceCosts().getText()).append("?").toString(), game)) { + Spell spell = game.getStack().getSpell(abilityToModify.getId()); + if (spell != null) { + SpellAbility splicedAbility = spliceCard.getSpellAbility().copy(); + splicedAbility.setSpellAbilityType(SpellAbilityType.SPLICE); + splicedAbility.setSourceId(abilityToModify.getSourceId()); + spell.addSpellAbility(splicedAbility); + for (Iterator it = ((SpliceOntoArcaneAbility) source).getSpliceCosts().iterator(); it.hasNext();) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCostsImpl) { + spell.getSpellAbility().getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } else { + spell.getSpellAbility().getCosts().add(cost.copy()); + } + } + // reveal the spliced card + controller.revealCards("Spliced card", new CardsImpl(spliceCard), game); + } + } + return true; + } + return false; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + MageObject object = game.getObject(abilityToModify.getSourceId()); + if (object != null && object.getSubtype().contains("Arcane")) { + return spliceSpellCanBeActivated(source, game); + } + return false; + } + + private boolean spliceSpellCanBeActivated(Ability source, Game game) { + // check if spell can be activated (protection problem not solved because effect will be used from the base spell?) + Card card = game.getCard(source.getSourceId()); + if (card != null) { + return card.getSpellAbility().canActivate(source.getControllerId(), game); + } + return false; + } +} diff --git a/Mage/src/mage/constants/EffectType.java b/Mage/src/mage/constants/EffectType.java index b83b5f3891c..aa483ea23c4 100644 --- a/Mage/src/mage/constants/EffectType.java +++ b/Mage/src/mage/constants/EffectType.java @@ -14,7 +14,8 @@ public enum EffectType { ASTHOUGH("As Though Effect"), RESTRICTION("Restriction Effect"), REQUIREMENT("Requirement Effect"), - COSTMODIFICATION("Cost Modification Effect"); + COSTMODIFICATION("Cost Modification Effect"), + SPLICE("Splice Card Effect"); private String text; diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 6e940915198..fab094b1409 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -118,9 +118,21 @@ public class Spell> implements StackObject, Card { public boolean activate(Game game, boolean noMana) { + if (!spellAbilities.get(0).activate(game, noMana)) { + return false; + } + // 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 (!spellAbility.activate(game, noMana)) { - return false; + // costs for spliced abilities were added to main spellAbility, so pay no man for spliced abilities + payNoMana |= spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE); + if (ignoreAbility) { + ignoreAbility = false; + } else { + if (!spellAbility.activate(game, payNoMana)) { + return false; + } } } return true; @@ -142,7 +154,9 @@ public class Spell> implements StackObject, Card { spellAbility.getModes().setMode(spellAbility.getModes().get(modeId)); if (spellAbility.getTargets().stillLegal(spellAbility, game)) { legalParts = true; - updateOptionalCosts(index); + if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) { + updateOptionalCosts(index); + } result |= spellAbility.resolve(game); } } @@ -351,6 +365,10 @@ public class Spell> implements StackObject, Card { return card.getOwnerId(); } + public void addSpellAbility(SpellAbility spellAbility) { + spellAbilities.add(spellAbility); + } + @Override public void addAbility(Ability ability) {}