diff --git a/Mage.Sets/src/mage/cards/m/MysticalDispute.java b/Mage.Sets/src/mage/cards/m/MysticalDispute.java index e290974dad..61c8d08775 100644 --- a/Mage.Sets/src/mage/cards/m/MysticalDispute.java +++ b/Mage.Sets/src/mage/cards/m/MysticalDispute.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.Collection; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; @@ -17,6 +15,9 @@ import mage.game.stack.StackObject; import mage.target.Target; import mage.target.TargetSpell; +import java.util.Collection; +import java.util.UUID; + /** * @author TheElk801 */ @@ -27,7 +28,7 @@ public final class MysticalDispute extends CardImpl { // This spell costs {2} less to cast if it targets a blue spell. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance) + Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Counter target spell unless its controller pays {3}. diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java index aa0fe9ebdb..df8dde261f 100644 --- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java +++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java @@ -1,7 +1,5 @@ package mage.cards.n; -import java.util.Collection; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; @@ -21,6 +19,9 @@ import mage.game.stack.StackObject; import mage.target.Target; import mage.target.TargetStackObject; +import java.util.Collection; +import java.util.UUID; + /** * @author Rafbill */ @@ -41,7 +42,7 @@ public final class NotOfThisWorld extends CardImpl { this.getSpellAbility().addTarget(new TargetStackObject(filter)); // Not of This World costs {7} less to cast if it targets a spell or ability that targets a creature you control with power 7 or greater. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance).setCanWorksOnStackOnly(true))); } private NotOfThisWorld(final NotOfThisWorld card) { @@ -86,8 +87,8 @@ enum NotOfThisWorldCondition implements Condition { .flatMap(Collection::stream) .map(game::getPermanentOrLKIBattlefield) .anyMatch(permanent -> permanent != null && filter.match( - permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game - )); + permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game + )); } @Override diff --git a/Mage.Sets/src/mage/cards/p/PriceOfFame.java b/Mage.Sets/src/mage/cards/p/PriceOfFame.java index 68c180beaa..be37a6e773 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfFame.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfFame.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceTargetsPermanentCondition; @@ -16,6 +15,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author TheElk801 */ @@ -35,7 +36,7 @@ public final class PriceOfFame extends CardImpl { // This spell costs {2} less to cast if it targets a legendary creature. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SpellCostReductionSourceEffect(2, condition) + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Destroy target creature. diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java index 26db6aba9a..d2a8f044d1 100644 --- a/Mage.Sets/src/mage/cards/s/SavageStomp.java +++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceTargetsPermanentCondition; @@ -20,6 +19,8 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author TheElk801 */ @@ -35,7 +36,7 @@ public final class SavageStomp extends CardImpl { // Savage Stomp costs {2} less to cast if it targets a Dinosaur you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SpellCostReductionSourceEffect(2, condition) + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true)); // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java index cd733ed0f8..7ab2978d83 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java @@ -2,14 +2,13 @@ package org.mage.test.cards.cost.modification; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; /** * @author JayDi85 */ -public class CostReduceWithConditionTest extends CardTestPlayerBase { +public class CostReduceWithConditionTest extends CardTestPlayerBaseWithAIHelps { @Test public void test_PriceOfFame_Normal() { @@ -31,9 +30,9 @@ public class CostReduceWithConditionTest extends CardTestPlayerBase { } @Test - @Ignore - // TODO: implement workaround like putToStackAsNonPlayable for abilities, see https://github.com/magefree/mage/issues/6685 - public void test_PriceOfFame_Reduce() { + public void test_PriceOfFame_Reduce_Manual() { + // https://github.com/magefree/mage/issues/6685 + // {3}{B} // This spell costs {2} less to cast if it targets a legendary creature. // Destroy target creature. @@ -50,4 +49,26 @@ public class CostReduceWithConditionTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Anje Falkenrath", 1); } + + @Test + public void test_PriceOfFame_Reduce_AI() { + // https://github.com/magefree/mage/issues/6685 + + // {3}{B} + // This spell costs {2} less to cast if it targets a legendary creature. + // Destroy target creature. + addCard(Zone.HAND, playerA, "Price of Fame", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4 - 2); + addCard(Zone.BATTLEFIELD, playerB, "Anje Falkenrath", 1); + + // AI must see and play that cards too + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Anje Falkenrath", 1); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java index c043b741f9..53af0aca6b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java @@ -1,27 +1,28 @@ - - package mage.abilities.effects.common.cost; -import mage.constants.Duration; -import mage.constants.EffectType; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.CostModificationEffect; import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.EffectType; +import mage.constants.Outcome; import mage.game.Game; /** * Simple implementation of a {@link CostModificationEffect} offering simplified * construction to setup the object for use by the mage framework. - + * * @author maurer.it_at_gmail.com */ public abstract class CostModificationEffectImpl extends ContinuousEffectImpl implements CostModificationEffect { private final CostModificationType modificationType; - - public CostModificationEffectImpl ( Duration duration, Outcome outcome, CostModificationType type) { + + // if effect need real stack object to check then mark it as stack only (example: apply cost reduction if you target human creature) + private boolean worksOnStackOnly = false; + + public CostModificationEffectImpl(Duration duration, Outcome outcome, CostModificationType type) { super(duration, outcome); this.effectType = EffectType.COSTMODIFICATION; this.modificationType = type; @@ -30,23 +31,33 @@ public abstract class CostModificationEffectImpl extends ContinuousEffectImpl im public CostModificationEffectImpl(final CostModificationEffectImpl effect) { super(effect); this.modificationType = effect.modificationType; + this.worksOnStackOnly = effect.worksOnStackOnly; } /** * Overridden and 'no-op' implementation put in place. * - * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability) - * * @param game * @param source * @return + * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability) */ @Override - public final boolean apply ( Game game, Ability source ) { return false; } - + public final boolean apply(Game game, Ability source) { + return false; + } + @Override - public CostModificationType getModificationType(){ + public CostModificationType getModificationType() { return this.modificationType; } + public CostModificationEffectImpl setCanWorksOnStackOnly(boolean worksOnStackOnly) { + this.worksOnStackOnly = worksOnStackOnly; + return this; + } + + public boolean canWorksOnStackOnly() { + return this.worksOnStackOnly; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java index 0f535c1a7f..a1910b5116 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java @@ -93,7 +93,9 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { - return condition == null || condition.apply(game, source); + // some conditions can works after put on stack, so skip it in get playable (allows user to put card on stack anyway) + boolean skipCondition = game.inCheckPlayableState() && canWorksOnStackOnly(); + return condition == null || skipCondition || condition.apply(game, source); } return false; }