From b54785688d1aeaa718a0b314a9324ddfa5e58ad1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 18 Aug 2014 23:53:50 +0200 Subject: [PATCH] Fixed Omniscience to work as alternate casting costs (also with split/fused cards). Added test. --- .../src/mage/sets/magic2013/Omniscience.java | 131 ++++++++++++------ .../test/cards/single/OmniscienceTest.java | 102 ++++++++++++++ .../costs/AlternativeCostSourceAbility.java | 33 +++-- 3 files changed, 212 insertions(+), 54 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java diff --git a/Mage.Sets/src/mage/sets/magic2013/Omniscience.java b/Mage.Sets/src/mage/sets/magic2013/Omniscience.java index 6a9ad57922c..13efe46d94f 100644 --- a/Mage.Sets/src/mage/sets/magic2013/Omniscience.java +++ b/Mage.Sets/src/mage/sets/magic2013/Omniscience.java @@ -27,22 +27,22 @@ */ package mage.sets.magic2013; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.abilities.keyword.FlashbackAbility; -import mage.abilities.keyword.RetraceAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.constants.*; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; - import java.util.UUID; -import mage.util.CardUtil; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterNonlandCard; +import mage.game.Game; +import mage.players.Player; /** * @@ -57,7 +57,7 @@ public class Omniscience extends CardImpl { this.color.setBlue(true); // You may cast nonland cards from your hand without paying their mana costs. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OmniscienceEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OmniscienceCastingEffect())); } public Omniscience(final Omniscience card) { @@ -70,49 +70,90 @@ public class Omniscience extends CardImpl { } } -class OmniscienceEffect extends CostModificationEffectImpl { +//class OmniscienceEffect extends CostModificationEffectImpl { +// +// public OmniscienceEffect() { +// super(Duration.WhileOnBattlefield, Outcome.PlayForFree, CostModificationType.SET_COST); +// this.staticText = "You may cast nonland cards from your hand without paying their mana costs"; +// } +// +// private OmniscienceEffect(final OmniscienceEffect effect) { +// super(effect); +// } +// +// @Override +// public boolean apply(Game game, Ability source, Ability abilityToModify) { +// SpellAbility spellAbility = (SpellAbility) abilityToModify; +// spellAbility.getManaCostsToPay().clear(); +// return true; +// } +// +// @Override +// public boolean applies(Ability abilityToModify, Ability source, Game game) { +// if (abilityToModify instanceof SpellAbility || abilityToModify instanceof FlashbackAbility || abilityToModify instanceof RetraceAbility) { +// Card sourceCard = game.getCard(abilityToModify.getSourceId()); +// StackObject stackObject = game.getStack().getStackObject(abilityToModify.getSourceId()); +// if (stackObject != null && stackObject instanceof Spell) { +// Zone zone = ((Spell)stackObject).getFromZone(); +// if (zone != null && zone.equals(Zone.HAND)) { +// if (sourceCard != null && sourceCard.getOwnerId().equals(source.getControllerId()) +// && !sourceCard.getCardType().contains(CardType.LAND)) { +// Player player = game.getPlayer(source.getControllerId()); +// String message = "Cast " + sourceCard.getName() + " without paying its mana costs?"; +// if (player != null && +// (CardUtil.isCheckPlayableMode(abilityToModify) || player.chooseUse(outcome, message, game))) { +// return true; +// } +// } +// } +// } +// } +// return false; +// } +// +// @Override +// public OmniscienceEffect copy() { +// return new OmniscienceEffect(this); +// } +//} - public OmniscienceEffect() { - super(Duration.WhileOnBattlefield, Outcome.PlayForFree, CostModificationType.SET_COST); - this.staticText = "You may cast nonland cards from your hand without paying their mana costs"; + +class OmniscienceCastingEffect extends ContinuousEffectImpl { + + static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility( + null, null, null, new FilterNonlandCard()); + + public OmniscienceCastingEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "You may cast nonland cards from your hand without paying their mana costs"; } - private OmniscienceEffect(final OmniscienceEffect effect) { + public OmniscienceCastingEffect(final OmniscienceCastingEffect effect) { super(effect); } @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - spellAbility.getManaCostsToPay().clear(); - return true; + public OmniscienceCastingEffect copy() { + return new OmniscienceCastingEffect(this); } @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof SpellAbility || abilityToModify instanceof FlashbackAbility || abilityToModify instanceof RetraceAbility) { - Card sourceCard = game.getCard(abilityToModify.getSourceId()); - StackObject stackObject = game.getStack().getStackObject(abilityToModify.getSourceId()); - if (stackObject != null && stackObject instanceof Spell) { - Zone zone = ((Spell)stackObject).getFromZone(); - if (zone != null && zone.equals(Zone.HAND)) { - if (sourceCard != null && sourceCard.getOwnerId().equals(source.getControllerId()) - && !sourceCard.getCardType().contains(CardType.LAND)) { - Player player = game.getPlayer(source.getControllerId()); - String message = "Cast " + sourceCard.getName() + " without paying its mana costs?"; - if (player != null && - (CardUtil.isCheckPlayableMode(abilityToModify) || player.chooseUse(outcome, message, game))) { - return true; - } - } - } - } + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility); + return true; } return false; } @Override - public OmniscienceEffect copy() { - return new OmniscienceEffect(this); + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java new file mode 100644 index 00000000000..9a7c0601706 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java @@ -0,0 +1,102 @@ +/* + * 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 org.mage.test.cards.single; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class OmniscienceTest extends CardTestPlayerBase { + + /** + * Omniscience {7}{U}{U}{U} + * + * Enchantment + * You may cast nonland cards from your hand without paying their mana costs. + * + */ + + @Test + public void testCastingCreature() { + addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); + + /* player.getPlayable does not take alternate + casting costs in account, so for the test the mana has to be available + but won't be used + */ + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + + 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", false); + } + + @Test + public void testCastingSplitCards() { + addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. + addCard(Zone.HAND, playerA, "Fire // Ice"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire", playerB); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Fire // Ice", 1); + + assertLife(playerA, 20); + assertLife(playerB, 18); + + assertTapped("Island", false); + assertTapped("Mountain", false); + } + +} diff --git a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java index 7ff7e03ea4d..8430dc66ff4 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -29,12 +29,14 @@ 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; +import mage.cards.Card; +import mage.constants.AbilityType; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; @@ -47,6 +49,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter Costs alternateCosts = new CostsImpl<>(); protected Condition condition; protected String rule; + protected FilterCard filter; public AlternativeCostSourceAbility(Cost cost) { this(cost, null); @@ -61,11 +64,24 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } public AlternativeCostSourceAbility(Cost cost, Condition condition, String rule) { + this(cost, condition, rule, null); + } + + public AlternativeCostSourceAbility(Cost cost, Condition condition, String rule, FilterCard filter) { super(Zone.ALL, null); this.convertToAlternativeCostAndAdd(cost); this.setRuleAtTheTop(true); this.condition = condition; this.rule = rule; + this.filter = filter; + } + + public AlternativeCostSourceAbility(final AlternativeCostSourceAbility ability) { + super(ability); + this.alternateCosts = ability.alternateCosts; + this.condition = ability.condition; + this.rule = ability.rule; + this.filter = ability.filter; } @Override @@ -80,13 +96,6 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } } - public AlternativeCostSourceAbility(final AlternativeCostSourceAbility ability) { - super(ability); - this.alternateCosts = ability.alternateCosts; - this.condition = ability.condition; - this.rule = ability.rule; - } - @Override public AlternativeCostSourceAbility copy() { return new AlternativeCostSourceAbility(this); @@ -102,7 +111,13 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter @Override public boolean askToActivateAlternativeCosts(Ability ability, Game game) { - if (ability instanceof SpellAbility) { + if (ability != null && AbilityType.SPELL.equals(ability.getAbilityType())) { + if (filter != null) { + Card card = game.getCard(ability.getSourceId()); + if (!filter.match(card, ability.getSourceId(), ability.getControllerId(), game)) { + return false; + } + } Player player = game.getPlayer(ability.getControllerId()); if (player != null) { if (alternateCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) &&