* Fixed that it was not possible to play spells without costs with alternative costs (e.g. Ancestral Visions with Omniscience). Fixed that playing spells with alternate costs did also remove additional costs (e.g. card with entwine cast with Omniscience).

This commit is contained in:
LevelX2 2015-10-20 00:44:32 +02:00
parent 7c35a69360
commit 606bf4d6e0
8 changed files with 150 additions and 32 deletions

View file

@ -35,7 +35,6 @@ import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
/**
*
@ -47,11 +46,10 @@ public class FutureSight extends CardImpl {
super(ownerId, 84, "Future Sight", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}{U}");
this.expansionSetCode = "ONS";
// Play with the top card of your library revealed.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayWithTheTopCardRevealedEffect()));
// You may play the top card of your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect(new FilterCard())));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayTheTopCardEffect()));
}
public FutureSight(final FutureSight card) {

View file

@ -115,7 +115,7 @@ public class SpliceOnArcaneTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
// You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost.
// You gain X life.
addCard(Zone.HAND, playerA, "Nourishing Shoal", 1);
addCard(Zone.HAND, playerA, "Nourishing Shoal", 1); // {X}{G}{G}
addCard(Zone.HAND, playerA, "Giant Growth", 1);
// You may put a creature card from your hand onto the battlefield. That creature gains haste. Sacrifice that creature at the beginning of the next end step.
// Splice onto Arcane {2}{R}{R} (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.)

View file

@ -192,4 +192,104 @@ public class OmniscienceTest extends CardTestPlayerBase {
assertGraveyardCount(playerB, "Pillarfield Ox", 1);
}
/**
* If another effect (e.g. Future Sight) allows you to cast nonland cards
* from zones other than your hand, Xmage incorrectly lets you cast those
* cards without paying their mana costs. Omniscience only lets you cast
* spells from your hand without paying their mana costs.
*/
@Test
public void testCastingWithFutureSight() {
// You may cast nonland cards from your hand without paying their mana costs.
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
// Play with the top card of your library revealed.
// You may play the top card of your library.
addCard(Zone.BATTLEFIELD, playerA, "Future Sight", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 1);
skipInitShuffling();
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertTapped("Plains", true); // plains have to be tapped because {2} have to be paid
}
/**
* If a spell has an additional cost (optional or mandatory, e.g. Entwine),
* Omniscience incorrectly allows you cast the spell as if that cost had
* been paid without paying that spell's mana cost. 117.9d If an alternative
* cost is being paid to cast a spell, any additional costs, cost increases,
* and cost reductions that affect that spell are applied to that
* alternative cost. (See rule 601.2f.)
*/
@Test
public void testCastingWithCyclonicRiftWithOverload() {
// You may cast nonland cards from your hand without paying their mana costs.
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
// Choose one - Barbed Lightning deals 3 damage to target creature; or Barbed Lightning deals 3 damage to target player.
// Entwine {2} (Choose both if you pay the entwine cost.)
addCard(Zone.HAND, playerA, "Barbed Lightning", 1);
// Creature - 3/3 Swampwalk
addCard(Zone.BATTLEFIELD, playerB, "Bog Wraith", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barbed Lightning", "Bog Wraith");
addTarget(playerA, playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Barbed Lightning", 1);
assertGraveyardCount(playerB, "Bog Wraith", 1);
assertLife(playerA, 20);
assertLife(playerB, 17);
assertTapped("Plains", true); // plains have to be tapped because {2} from Entwine have to be paid
}
/**
* If a spell has an unpayable cost (e.g. Ancestral Vision, which has no
* mana cost), Omniscience should allow you to cast that spell without
* paying its mana cost. In the case of Ancestral Vision, for example, Xmage
* only gives you the option to suspend Ancestral Vision. 117.6a If an
* unpayable cost is increased by an effect or an additional cost is
* imposed, the cost is still unpayable. If an alternative cost is applied
* to an unpayable cost, including an effect that allows a player to cast a
* spell without paying its mana cost, the alternative cost may be paid.
*/
@Test
public void testCastingUnpayableCost() {
// You may cast nonland cards from your hand without paying their mana costs.
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
// Suspend 4-{U}
// Target player draws three cards.
addCard(Zone.HAND, playerA, "Ancestral Vision", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Vision", playerA);
addTarget(playerA, playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Ancestral Vision", 1);
assertHandCount(playerA, 3);
assertLife(playerA, 20);
assertLife(playerB, 20);
}
}

View file

@ -471,7 +471,10 @@ public abstract class AbilityImpl implements Ability {
}
// controller specific alternate spell costs
if (!noMana && !alternativeCostisUsed) {
if (this.getAbilityType().equals(AbilityType.SPELL)) {
if (this.getAbilityType().equals(AbilityType.SPELL)
// 117.9a Only one alternative cost can be applied to any one spell as its being cast.
// So an alternate spell ability can't be paid with Omniscience
&& !((SpellAbility) this).getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) {
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {
if (alternativeSourceCosts.isAvailable(this, game)) {
if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) {

View file

@ -99,10 +99,6 @@ public class SpellAbility extends ActivatedAbilityImpl {
&& !controllerId.equals(playerId)) {
return false;
}
// Check if spell has no costs (not {0} mana costs), than it's not castable. E.g. for spells like Living End, that only can be cast by Suspend Ability.
if (this.getManaCosts().isEmpty() && this.getCosts().isEmpty()) {
return false;
}
// Check if rule modifying events prevent to cast the spell in check playable mode
if (this.isCheckPlayableMode()) {
if (game.getContinuousEffects().preventedByRuleModification(

View file

@ -29,6 +29,7 @@ package mage.abilities.costs;
import java.util.Iterator;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.mana.ManaCost;
@ -39,6 +40,7 @@ import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
@ -145,28 +147,39 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
}
Player player = game.getPlayer(ability.getControllerId());
if (player != null) {
Costs<AlternativeCost2> alternativeCosts;
Costs<AlternativeCost2> alternativeCostsToCheck;
if (dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(ability, game)));
alternativeCostsToCheck = new CostsImpl<>();
alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(ability, game)));
} else {
alternativeCosts = this.alternateCosts;
alternativeCostsToCheck = this.alternateCosts;
}
String costChoiceText;
if (dynamicCost != null) {
costChoiceText = dynamicCost.getText(ability, game);
} else {
costChoiceText = alternativeCosts.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCosts.getText() + ")";
costChoiceText = alternativeCostsToCheck.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCostsToCheck.getText() + ")";
}
if (alternativeCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
if (alternativeCostsToCheck.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
&& player.chooseUse(Outcome.Benefit, costChoiceText, this, game)) {
ability.getManaCostsToPay().clear();
if (ability instanceof SpellAbility) {
for (Iterator<ManaCost> iterator = ability.getManaCostsToPay().iterator(); iterator.hasNext();) {
ManaCost manaCost = iterator.next();
if (manaCost instanceof VariableCost) {
iterator.remove();
}
}
CardUtil.reduceCost((SpellAbility) ability, ability.getManaCosts());
} else {
ability.getManaCostsToPay().clear();
}
if (!onlyMana) {
ability.getCosts().clear();
}
for (Cost cost : alternativeCosts) {
for (Cost cost : alternativeCostsToCheck) {
AlternativeCost2 alternateCost = (AlternativeCost2) cost;
alternateCost.activate();
for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext();) {
@ -190,14 +203,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
@Override
public boolean isActivated(Ability source, Game game) {
Costs<AlternativeCost2> alternativeCosts;
Costs<AlternativeCost2> alternativeCostsToCheck;
if (dynamicCost != null) {
alternativeCosts = new CostsImpl<>();
alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(source, game)));
alternativeCostsToCheck = new CostsImpl<>();
alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(source, game)));
} else {
alternativeCosts = this.alternateCosts;
alternativeCostsToCheck = this.alternateCosts;
}
for (AlternativeCost2 cost : alternativeCosts) {
for (AlternativeCost2 cost : alternativeCostsToCheck) {
if (cost.isActivated(game)) {
return true;
}

View file

@ -111,7 +111,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
if (player != null) {
this.resetCosts();
if (additionalCost != null) {
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
if (player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
additionalCost.activate();
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
Cost cost = (Cost) it.next();

View file

@ -2317,15 +2317,23 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
}
ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions();
if (abilityOptions.size() == 0) {
return true;
} else {
for (Mana mana : abilityOptions) {
for (Mana avail : available) {
if (mana.enough(avail)) {
return true;
boolean canBeCastRegularly = true;
if (copy instanceof SpellAbility && copy.getManaCosts().isEmpty() && copy.getCosts().isEmpty()) {
// 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost...
// 117.6a (...) If an alternative cost is applied to an unpayable cost,
// including an effect that allows a player to cast a spell without paying its mana cost, the alternative cost may be paid.
canBeCastRegularly = false;
}
if (canBeCastRegularly) {
ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions();
if (abilityOptions.size() == 0) {
return true;
} else {
for (Mana mana : abilityOptions) {
for (Mana avail : available) {
if (mana.enough(avail)) {
return true;
}
}
}
}