mirror of
https://github.com/correl/mage.git
synced 2025-01-12 19:25:44 +00:00
Added SpliceOntoArcane Ability. It's working but it will need some handling improvements.
This commit is contained in:
parent
2aa984d4d6
commit
725ee6e042
8 changed files with 470 additions and 15 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
66
Mage/src/mage/abilities/effects/SpliceCardEffect.java
Normal file
66
Mage/src/mage/abilities/effects/SpliceCardEffect.java
Normal 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);
|
||||
}
|
67
Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java
Normal file
67
Mage/src/mage/abilities/effects/SpliceCardEffectImpl.java
Normal 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; }
|
||||
}
|
|
@ -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;
|
||||
|
|
210
Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java
Normal file
210
Mage/src/mage/abilities/keyword/SpliceOntoArcaneAbility.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue