Added SpliceOntoArcane Ability. It's working but it will need some handling improvements.

This commit is contained in:
LevelX2 2013-06-26 17:34:14 +02:00
parent 2aa984d4d6
commit 725ee6e042
8 changed files with 470 additions and 15 deletions

View file

@ -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<T extends AbilityImpl<T>> 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<T extends AbilityImpl<T>> 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<T extends AbilityImpl<T>> 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.

View file

@ -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<RestrictionEffect> restrictionEffects = new ContinuousEffectsList<RestrictionEffect>();
private ContinuousEffectsList<AsThoughEffect> asThoughEffects = new ContinuousEffectsList<AsThoughEffect>();
private ContinuousEffectsList<CostModificationEffect> costModificationEffects = new ContinuousEffectsList<CostModificationEffect>();
private ContinuousEffectsList<SpliceCardEffect> spliceCardEffects = new ContinuousEffectsList<SpliceCardEffect>();
private List<ContinuousEffectsList<?>> allEffectsLists = new ArrayList<ContinuousEffectsList<?>>();
@ -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<UUID, UUID> 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<ContinuousEffect> 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<SpliceCardEffect> getApplicableSpliceCardEffects(Game game) {
List<SpliceCardEffect> spliceEffects = new ArrayList<SpliceCardEffect>();
for (SpliceCardEffect effect: spliceCardEffects) {
HashSet<Ability> 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<AsThoughEffect> 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<SpliceCardEffect> spliceEffects = getApplicableSpliceCardEffects(game);
for ( SpliceCardEffect effect : spliceEffects) {
HashSet<Ability> 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<UUID, HashSet<UUID>> consumed = new HashMap<UUID, HashSet<UUID>>();
@ -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);

View file

@ -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 <T>
* @author levelX2
*/
public interface SpliceCardEffect<T extends SpliceCardEffect<T>> extends ContinuousEffect<T> {
/**
* 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);
}

View file

@ -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<T extends SpliceCardEffectImpl<T>> extends ContinuousEffectImpl<T> implements SpliceCardEffect<T> {
public SpliceCardEffectImpl ( Duration duration, Outcome outcome ) {
super(duration, outcome);
this.effectType = EffectType.SPLICE;
}
public SpliceCardEffectImpl(final SpliceCardEffectImpl<T> 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; }
}

View file

@ -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<GainAbilityTargetEffect> {
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<GainAbilityTar
@Override
public boolean apply(Game game, Ability source) {
int affectedTargets = 0;
for (UUID permanentId : targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
permanent.addAbility(ability, source.getSourceId(), game);
affectedTargets++;
if (onCard) {
for (UUID cardId : targetPointer.getTargets(game, source)) {
Card card = game.getCard(cardId);
if (card != null) {
card.addAbility(ability);
affectedTargets++;
}
}
if (duration.equals(Duration.OneUse)) {
discard();
}
} else {
for (UUID permanentId : targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
permanent.addAbility(ability, source.getSourceId(), game);
affectedTargets++;
}
}
}
return affectedTargets > 0;

View file

@ -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(" <i>(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.)</i>");
return sb.toString();
}
}
class SpliceOntoArcaneEffect extends SpliceCardEffectImpl<SpliceOntoArcaneEffect> {
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;
}
}

View file

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

View file

@ -118,9 +118,21 @@ public class Spell<T extends Spell<T>> 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<T extends Spell<T>> 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<T extends Spell<T>> implements StackObject, Card {
return card.getOwnerId();
}
public void addSpellAbility(SpellAbility spellAbility) {
spellAbilities.add(spellAbility);
}
@Override
public void addAbility(Ability ability) {}