mirror of
https://github.com/correl/mage.git
synced 2024-11-28 19:19:55 +00:00
Alternative cost - fixed that it doesn't allow to cast cards that was affected by cost modification effects (example: Prowl ability, see #6698);
This commit is contained in:
parent
f9a9a55f7b
commit
1e744a0aae
12 changed files with 218 additions and 114 deletions
|
@ -1,21 +1,19 @@
|
||||||
|
|
||||||
package mage.cards.e;
|
package mage.cards.e;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.condition.common.ProwlCondition;
|
import mage.abilities.condition.common.ProwlCostWasPaidCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.hint.common.ProwlCostWasPaidHint;
|
||||||
import mage.abilities.keyword.ProwlAbility;
|
import mage.abilities.keyword.ProwlAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
@ -23,14 +21,16 @@ import mage.players.Player;
|
||||||
import mage.target.common.TargetCardInLibrary;
|
import mage.target.common.TargetCardInLibrary;
|
||||||
import mage.target.common.TargetOpponent;
|
import mage.target.common.TargetOpponent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class EarwigSquad extends CardImpl {
|
public final class EarwigSquad extends CardImpl {
|
||||||
|
|
||||||
public EarwigSquad(UUID ownerId, CardSetInfo setInfo) {
|
public EarwigSquad(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
|
||||||
this.subtype.add(SubType.GOBLIN);
|
this.subtype.add(SubType.GOBLIN);
|
||||||
this.subtype.add(SubType.ROGUE);
|
this.subtype.add(SubType.ROGUE);
|
||||||
|
|
||||||
|
@ -39,11 +39,13 @@ public final class EarwigSquad extends CardImpl {
|
||||||
|
|
||||||
// Prowl {2}{B}
|
// Prowl {2}{B}
|
||||||
this.addAbility(new ProwlAbility(this, "{2}{B}"));
|
this.addAbility(new ProwlAbility(this, "{2}{B}"));
|
||||||
|
|
||||||
// When Earwig Squad enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.
|
// When Earwig Squad enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.
|
||||||
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new EarwigSquadEffect(), false);
|
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new EarwigSquadEffect(), false);
|
||||||
ability.addTarget(new TargetOpponent());
|
ability.addTarget(new TargetOpponent());
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCondition.instance,
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCostWasPaidCondition.instance,
|
||||||
"When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library."));
|
"When {this} enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library.")
|
||||||
|
.addHint(ProwlCostWasPaidHint.instance));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@ class EarwigSquadEffect extends OneShotEffect {
|
||||||
Player opponent = game.getPlayer(source.getFirstTarget());
|
Player opponent = game.getPlayer(source.getFirstTarget());
|
||||||
Player player = game.getPlayer(source.getControllerId());
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
if (player != null && opponent != null) {
|
if (player != null && opponent != null) {
|
||||||
TargetCardInLibrary target = new TargetCardInLibrary(0, 3, new FilterCard("cards from opponents library to exile"));
|
TargetCardInLibrary target = new TargetCardInLibrary(0, 3, new FilterCard("cards from opponents library to exile"));
|
||||||
if (player.searchLibrary(target, source, game, opponent.getId())) {
|
if (player.searchLibrary(target, source, game, opponent.getId())) {
|
||||||
List<UUID> targets = target.getTargets();
|
List<UUID> targets = target.getTargets();
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
|
||||||
package mage.cards.l;
|
package mage.cards.l;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.condition.common.ProwlCondition;
|
import mage.abilities.condition.common.ProwlCostWasPaidCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.hint.common.ProwlCostWasPaidHint;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
import mage.abilities.keyword.ProwlAbility;
|
import mage.abilities.keyword.ProwlAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
@ -14,14 +13,15 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class LatchkeyFaerie extends CardImpl {
|
public final class LatchkeyFaerie extends CardImpl {
|
||||||
|
|
||||||
public LatchkeyFaerie(UUID ownerId, CardSetInfo setInfo) {
|
public LatchkeyFaerie(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
|
||||||
this.subtype.add(SubType.FAERIE);
|
this.subtype.add(SubType.FAERIE);
|
||||||
this.subtype.add(SubType.ROGUE);
|
this.subtype.add(SubType.ROGUE);
|
||||||
|
|
||||||
|
@ -36,8 +36,9 @@ public final class LatchkeyFaerie extends CardImpl {
|
||||||
|
|
||||||
// When Latchkey Faerie enters the battlefield, if its prowl cost was paid, draw a card.
|
// When Latchkey Faerie enters the battlefield, if its prowl cost was paid, draw a card.
|
||||||
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false);
|
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false);
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCondition.instance,
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, ProwlCostWasPaidCondition.instance,
|
||||||
"When {this} enters the battlefield, if its prowl cost was paid, draw a card."));
|
"When {this} enters the battlefield, if its prowl cost was paid, draw a card.")
|
||||||
|
.addHint(ProwlCostWasPaidHint.instance));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
|
|
||||||
package mage.cards.m;
|
package mage.cards.m;
|
||||||
|
|
||||||
import java.util.UUID;
|
import mage.abilities.condition.common.ProwlCostWasPaidCondition;
|
||||||
import mage.abilities.condition.common.ProwlCondition;
|
|
||||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
import mage.abilities.effects.common.GainLifeEffect;
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
import mage.abilities.effects.common.LoseLifeTargetEffect;
|
import mage.abilities.effects.common.LoseLifeTargetEffect;
|
||||||
|
import mage.abilities.hint.common.ProwlCostWasPaidHint;
|
||||||
import mage.abilities.keyword.ProwlAbility;
|
import mage.abilities.keyword.ProwlAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
@ -15,14 +14,15 @@ import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.target.TargetPlayer;
|
import mage.target.TargetPlayer;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class MorselTheft extends CardImpl {
|
public final class MorselTheft extends CardImpl {
|
||||||
|
|
||||||
public MorselTheft(UUID ownerId, CardSetInfo setInfo) {
|
public MorselTheft(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{2}{B}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{2}{B}{B}");
|
||||||
this.subtype.add(SubType.ROGUE);
|
this.subtype.add(SubType.ROGUE);
|
||||||
|
|
||||||
// Prowl {1}{B}
|
// Prowl {1}{B}
|
||||||
|
@ -34,7 +34,8 @@ public final class MorselTheft extends CardImpl {
|
||||||
effect.setText("and you gain 3 life");
|
effect.setText("and you gain 3 life");
|
||||||
getSpellAbility().addEffect(effect);
|
getSpellAbility().addEffect(effect);
|
||||||
getSpellAbility().addTarget(new TargetPlayer());
|
getSpellAbility().addTarget(new TargetPlayer());
|
||||||
getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), ProwlCondition.instance));
|
getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), ProwlCostWasPaidCondition.instance));
|
||||||
|
getSpellAbility().addHint(ProwlCostWasPaidHint.instance);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,47 @@
|
||||||
|
|
||||||
package mage.cards.n;
|
package mage.cards.n;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.condition.common.ProwlCondition;
|
import mage.abilities.condition.common.ProwlCostWasPaidCondition;
|
||||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect;
|
import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect;
|
||||||
|
import mage.abilities.hint.common.ProwlCostWasPaidHint;
|
||||||
import mage.abilities.keyword.ProwlAbility;
|
import mage.abilities.keyword.ProwlAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.token.FaerieRogueToken;
|
import mage.game.permanent.token.FaerieRogueToken;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.watchers.common.AmountOfDamageAPlayerReceivedThisTurnWatcher;
|
import mage.watchers.common.AmountOfDamageAPlayerReceivedThisTurnWatcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LoneFox
|
* @author LoneFox
|
||||||
*/
|
*/
|
||||||
public final class NotoriousThrong extends CardImpl {
|
public final class NotoriousThrong extends CardImpl {
|
||||||
|
|
||||||
public NotoriousThrong(UUID ownerId, CardSetInfo setInfo) {
|
public NotoriousThrong(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{3}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{3}{U}");
|
||||||
this.subtype.add(SubType.ROGUE);
|
this.subtype.add(SubType.ROGUE);
|
||||||
|
|
||||||
// Prowl {5}{U}
|
// Prowl {5}{U}
|
||||||
this.addAbility(new ProwlAbility(this, "{5}{U}"));
|
this.addAbility(new ProwlAbility(this, "{5}{U}"));
|
||||||
|
|
||||||
// create X 1/1 black Faerie Rogue creature tokens with flying, where X is the damage dealt to your opponents this turn.
|
// create X 1/1 black Faerie Rogue creature tokens with flying, where X is the damage dealt to your opponents this turn.
|
||||||
this.getSpellAbility().addEffect(new NotoriousThrongEffect());
|
this.getSpellAbility().addEffect(new NotoriousThrongEffect());
|
||||||
this.getSpellAbility().addWatcher(new AmountOfDamageAPlayerReceivedThisTurnWatcher());
|
this.getSpellAbility().addWatcher(new AmountOfDamageAPlayerReceivedThisTurnWatcher());
|
||||||
|
|
||||||
// If Notorious Throng's prowl cost was paid, take an extra turn after this one.
|
// If Notorious Throng's prowl cost was paid, take an extra turn after this one.
|
||||||
Effect effect = new ConditionalOneShotEffect(new AddExtraTurnControllerEffect(), ProwlCondition.instance);
|
Effect effect = new ConditionalOneShotEffect(new AddExtraTurnControllerEffect(), ProwlCostWasPaidCondition.instance);
|
||||||
effect.setText("If {this}'s prowl cost was paid, take an extra turn after this one.");
|
effect.setText("If {this}'s prowl cost was paid, take an extra turn after this one.");
|
||||||
this.getSpellAbility().addEffect(effect);
|
this.getSpellAbility().addEffect(effect);
|
||||||
|
this.getSpellAbility().addHint(ProwlCostWasPaidHint.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NotoriousThrong(final NotoriousThrong card) {
|
public NotoriousThrong(final NotoriousThrong card) {
|
||||||
|
@ -71,12 +74,12 @@ class NotoriousThrongEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
AmountOfDamageAPlayerReceivedThisTurnWatcher watcher = game.getState().getWatcher(AmountOfDamageAPlayerReceivedThisTurnWatcher.class);
|
AmountOfDamageAPlayerReceivedThisTurnWatcher watcher = game.getState().getWatcher(AmountOfDamageAPlayerReceivedThisTurnWatcher.class);
|
||||||
if(controller != null && watcher != null) {
|
if (controller != null && watcher != null) {
|
||||||
int numTokens = 0;
|
int numTokens = 0;
|
||||||
for(UUID opponentId: game.getOpponents(controller.getId())) {
|
for (UUID opponentId : game.getOpponents(controller.getId())) {
|
||||||
numTokens += watcher.getAmountOfDamageReceivedThisTurn(opponentId);
|
numTokens += watcher.getAmountOfDamageReceivedThisTurn(opponentId);
|
||||||
}
|
}
|
||||||
if(numTokens > 0) {
|
if (numTokens > 0) {
|
||||||
new CreateTokenEffect(new FaerieRogueToken(), numTokens).apply(game, source);
|
new CreateTokenEffect(new FaerieRogueToken(), numTokens).apply(game, source);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,75 +1,77 @@
|
||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.mage.test.cards.abilities.keywords;
|
package org.mage.test.cards.abilities.keywords;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
|
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
|
||||||
*/
|
*/
|
||||||
public class ProwlTest extends CardTestPlayerBase {
|
public class ProwlTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
@Ignore // have not figured out how to have the test API cast a card using Prowl yet
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicProwlCasting() {
|
public void test_ProwlNormal() {
|
||||||
// Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1)
|
// Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1)
|
||||||
// Auntie's Snitch can't block.
|
// Auntie's Snitch can't block.
|
||||||
// Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.)
|
// Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.)
|
||||||
// Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand.
|
// Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand.
|
||||||
addCard(Zone.HAND, playerA, "Auntie's Snitch");
|
addCard(Zone.HAND, playerA, "Auntie's Snitch");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||||
|
//
|
||||||
// {1}{R} Creature — Goblin Warrior 1/1
|
// {1}{R} Creature — Goblin Warrior 1/1
|
||||||
// Red creatures you control have first strike.
|
// Red creatures you control have first strike.
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Bloodmark Mentor");
|
addCard(Zone.BATTLEFIELD, playerA, "Bloodmark Mentor");
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
|
||||||
|
// prepare prowl condition
|
||||||
|
checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Auntie's Snitch", false);
|
||||||
attack(1, playerA, "Bloodmark Mentor");
|
attack(1, playerA, "Bloodmark Mentor");
|
||||||
|
|
||||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch using prowl");
|
checkPlayableAbility("must play", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Auntie's Snitch", true);
|
||||||
|
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch");
|
||||||
setChoice(playerA, "Yes"); // choosing to pay prowl cost
|
setChoice(playerA, "Yes"); // choosing to pay prowl cost
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertLife(playerB, 19);
|
assertLife(playerB, 19);
|
||||||
assertPermanentCount(playerA, "Bloodmark Mentor", 1);
|
assertPermanentCount(playerA, "Bloodmark Mentor", 1);
|
||||||
assertPermanentCount(playerA, "Auntie's Snitch", 1);
|
assertPermanentCount(playerA, "Auntie's Snitch", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reported bug: Prowl is not taking into consideration other cost reducing effects. For instance Goblin Warchief
|
* Reported bug: Prowl is not taking into consideration other cost reducing effects. For instance Goblin Warchief
|
||||||
* does not reduce the Prowl cost of other Goblin cards with Prowl ability.
|
* does not reduce the Prowl cost of other Goblin cards with Prowl ability.
|
||||||
*/
|
*/
|
||||||
@Ignore // have not figured out how to have the test API cast a card using Prowl yet
|
|
||||||
@Test
|
@Test
|
||||||
public void testProwlWithCostDiscount() {
|
public void test_ProwlWithCostReduce() {
|
||||||
|
|
||||||
// Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1)
|
// Auntie's Snitch {2}{B} Creature — Goblin Rogue (3/1)
|
||||||
// Auntie's Snitch can't block.
|
// Auntie's Snitch can't block.
|
||||||
// Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.)
|
// Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Goblin or Rogue.)
|
||||||
// Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand.
|
// Whenever a Goblin or Rogue you control deals combat damage to a player, if Auntie's Snitch is in your graveyard, you may return Auntie's Snitch to your hand.
|
||||||
addCard(Zone.HAND, playerA, "Auntie's Snitch");
|
addCard(Zone.HAND, playerA, "Auntie's Snitch");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||||
|
|
||||||
// Goblin Warchief {1}{R}{R} Creature — Goblin Warrior (2/2)
|
// Goblin Warchief {1}{R}{R} Creature — Goblin Warrior (2/2)
|
||||||
// Goblin spells you cast cost 1 less to cast.
|
// Goblin spells you cast cost 1 less to cast.
|
||||||
// Goblin creatures you control have haste.
|
// Goblin creatures you control have haste.
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Goblin Warchief");
|
addCard(Zone.BATTLEFIELD, playerA, "Goblin Warchief");
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
|
||||||
|
// prepare prowl condition
|
||||||
|
checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Auntie's Snitch", false);
|
||||||
attack(1, playerA, "Goblin Warchief");
|
attack(1, playerA, "Goblin Warchief");
|
||||||
|
|
||||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch using prowl"); // should only cost {B} with Warchief discount
|
checkPlayableAbility("must play", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Auntie's Snitch", true);
|
||||||
|
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Auntie's Snitch"); // should only cost {B} with Warchief discount
|
||||||
setChoice(playerA, "Yes"); // choosing to pay prowl cost
|
setChoice(playerA, "Yes"); // choosing to pay prowl cost
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertLife(playerB, 18);
|
assertLife(playerB, 18);
|
||||||
assertPermanentCount(playerA, "Goblin Warchief", 1);
|
assertPermanentCount(playerA, "Goblin Warchief", 1);
|
||||||
assertPermanentCount(playerA, "Auntie's Snitch", 1);
|
assertPermanentCount(playerA, "Auntie's Snitch", 1);
|
||||||
|
|
|
@ -129,10 +129,15 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
attack(2, playerB, "Narset, Enlightened Master");
|
attack(2, playerB, "Narset, Enlightened Master");
|
||||||
|
|
||||||
|
checkPlayableAbility("must play", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cast Cathartic Reunion", true);
|
||||||
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cathartic Reunion");
|
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cathartic Reunion");
|
||||||
setChoice(playerB, "Swamp^Forest");
|
setChoice(playerB, "Swamp^Forest");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.END_TURN);
|
setStopAt(2, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
|
||||||
assertHandCount(playerB, 3);
|
assertHandCount(playerB, 3);
|
||||||
assertGraveyardCount(playerB, "Forest", 1);
|
assertGraveyardCount(playerB, "Forest", 1);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
|
|
||||||
package mage.abilities.condition.common;
|
package mage.abilities.condition.common;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
import mage.abilities.keyword.ProwlAbility;
|
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
import mage.watchers.common.ProwlWatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a the spell was cast with the alternate prowl costs
|
* Is it able to activate prowl cost (damage was made)
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
public enum ProwlCondition implements Condition {
|
public enum ProwlCondition implements Condition {
|
||||||
|
|
||||||
|
@ -18,22 +18,15 @@ public enum ProwlCondition implements Condition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
|
ProwlWatcher watcher = game.getState().getWatcher(ProwlWatcher.class);
|
||||||
Card card = game.getCard(source.getSourceId());
|
Card card = game.getCard(source.getSourceId());
|
||||||
if (card != null) {
|
if (watcher != null && card != null) {
|
||||||
for (Ability ability : card.getAbilities()) {
|
for (SubType subtype : card.getSubtype(game)) {
|
||||||
if (ability instanceof ProwlAbility) {
|
if (watcher.hasSubtypeMadeCombatDamage(source.getControllerId(), subtype)) {
|
||||||
if (((ProwlAbility) ability).isActivated(source, game)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "{source}'s prowl cost was paid";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package mage.abilities.condition.common;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.keyword.ProwlAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.game.Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a the spell was cast with the alternate prowl costs
|
||||||
|
*
|
||||||
|
* @author LevelX2
|
||||||
|
*/
|
||||||
|
public enum ProwlCostWasPaidCondition implements Condition {
|
||||||
|
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Card card = game.getCard(source.getSourceId());
|
||||||
|
if (card != null) {
|
||||||
|
for (Ability ability : card.getAbilities()) {
|
||||||
|
if (ability instanceof ProwlAbility) {
|
||||||
|
if (((ProwlAbility) ability).isActivated(source, game)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{source}'s prowl cost was paid";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package mage.abilities.hint.common;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.condition.common.ProwlCostWasPaidCondition;
|
||||||
|
import mage.abilities.hint.ConditionHint;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.game.Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public enum ProwlCostWasPaidHint implements Hint {
|
||||||
|
|
||||||
|
instance;
|
||||||
|
private static final ConditionHint hint = new ConditionHint(ProwlCostWasPaidCondition.instance, "Prowl cost was paid");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText(Game game, Ability ability) {
|
||||||
|
return hint.getText(game, ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Hint copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
26
Mage/src/main/java/mage/abilities/hint/common/ProwlHint.java
Normal file
26
Mage/src/main/java/mage/abilities/hint/common/ProwlHint.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package mage.abilities.hint.common;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.condition.common.ProwlCondition;
|
||||||
|
import mage.abilities.hint.ConditionHint;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.game.Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public enum ProwlHint implements Hint {
|
||||||
|
|
||||||
|
instance;
|
||||||
|
private static final ConditionHint hint = new ConditionHint(ProwlCondition.instance, "Prowl cost can be activated");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText(Game game, Ability ability) {
|
||||||
|
return hint.getText(game, ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Hint copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,26 @@
|
||||||
|
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.StaticAbility;
|
import mage.abilities.StaticAbility;
|
||||||
import mage.abilities.costs.AlternativeCost2;
|
import mage.abilities.condition.common.ProwlCondition;
|
||||||
import mage.abilities.costs.AlternativeCost2Impl;
|
import mage.abilities.costs.*;
|
||||||
import mage.abilities.costs.AlternativeSourceCosts;
|
|
||||||
import mage.abilities.costs.Cost;
|
|
||||||
import mage.abilities.costs.Costs;
|
|
||||||
import mage.abilities.costs.CostsImpl;
|
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.hint.common.ProwlHint;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.watchers.common.ProwlWatcher;
|
import mage.watchers.common.ProwlWatcher;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 702.74. Prowl #
|
* 702.74. Prowl #
|
||||||
*
|
* <p>
|
||||||
* 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]"
|
* 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]"
|
||||||
* means "You may pay [cost] rather than pay this spell's mana cost if a player
|
* means "You may pay [cost] rather than pay this spell's mana cost if a player
|
||||||
* was dealt combat damage this turn by a source that, at the time it dealt that
|
* was dealt combat damage this turn by a source that, at the time it dealt that
|
||||||
|
@ -41,13 +37,13 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
|
||||||
private String reminderText;
|
private String reminderText;
|
||||||
|
|
||||||
public ProwlAbility(Card card, String manaString) {
|
public ProwlAbility(Card card, String manaString) {
|
||||||
super(Zone.STACK, null);
|
super(Zone.ALL, null);
|
||||||
setRuleAtTheTop(true);
|
this.setRuleAtTheTop(true);
|
||||||
name = PROWL_KEYWORD;
|
this.name = PROWL_KEYWORD;
|
||||||
setReminderText(card);
|
this.setReminderText(card);
|
||||||
this.addProwlCost(manaString);
|
this.addProwlCost(manaString);
|
||||||
addWatcher(new ProwlWatcher());
|
this.addWatcher(new ProwlWatcher());
|
||||||
|
this.addHint(ProwlHint.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProwlAbility(final ProwlAbility ability) {
|
public ProwlAbility(final ProwlAbility ability) {
|
||||||
|
@ -85,26 +81,17 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable(Ability source, Game game) {
|
public boolean isAvailable(Ability source, Game game) {
|
||||||
return true;
|
return ProwlCondition.instance.apply(game, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
|
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
|
||||||
if (ability instanceof SpellAbility) {
|
if (ability instanceof SpellAbility) {
|
||||||
Player player = game.getPlayer(controllerId);
|
Player player = game.getPlayer(controllerId);
|
||||||
ProwlWatcher prowlWatcher = game.getState().getWatcher(ProwlWatcher.class);
|
if (player == null) {
|
||||||
Card card = game.getCard(ability.getSourceId());
|
return false;
|
||||||
if (player == null || prowlWatcher == null || card == null) {
|
|
||||||
throw new IllegalArgumentException("Params can't be null");
|
|
||||||
}
|
}
|
||||||
boolean canProwl = false;
|
if (ProwlCondition.instance.apply(game, ability)) {
|
||||||
for (SubType subtype : card.getSubtype(game)) {
|
|
||||||
if (prowlWatcher.hasSubtypeMadeCombatDamage(ability.getControllerId(), subtype)) {
|
|
||||||
canProwl = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (canProwl) {
|
|
||||||
this.resetProwl();
|
this.resetProwl();
|
||||||
for (AlternativeCost2 prowlCost : prowlCosts) {
|
for (AlternativeCost2 prowlCost : prowlCosts) {
|
||||||
if (prowlCost.canPay(ability, sourceId, controllerId, game)
|
if (prowlCost.canPay(ability, sourceId, controllerId, game)
|
||||||
|
@ -112,7 +99,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
|
||||||
prowlCost.activate();
|
prowlCost.activate();
|
||||||
ability.getManaCostsToPay().clear();
|
ability.getManaCostsToPay().clear();
|
||||||
ability.getCosts().clear();
|
ability.getCosts().clear();
|
||||||
for (Iterator it = ((Costs) prowlCost).iterator(); it.hasNext();) {
|
for (Iterator it = ((Costs) prowlCost).iterator(); it.hasNext(); ) {
|
||||||
Cost cost = (Cost) it.next();
|
Cost cost = (Cost) it.next();
|
||||||
if (cost instanceof ManaCostsImpl) {
|
if (cost instanceof ManaCostsImpl) {
|
||||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||||
|
@ -162,7 +149,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setReminderText(Card card) {
|
private void setReminderText(Card card) {
|
||||||
reminderText =
|
reminderText =
|
||||||
"(You may cast this for its prowl cost if you dealt combat damage to a player this turn with a creature that shared a creature type with {this}";
|
"(You may cast this for its prowl cost if you dealt combat damage to a player this turn with a creature that shared a creature type with {this}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,4 +161,4 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
|
||||||
}
|
}
|
||||||
return alterCosts;
|
return alterCosts;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3046,6 +3046,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions availableMana, Ability ability, Game game) {
|
protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions availableMana, Ability ability, Game game) {
|
||||||
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
||||||
|
Ability copyAbility; // for alternative cost and reduce tries
|
||||||
for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) {
|
for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) {
|
||||||
// if cast for noMana no Alternative costs are allowed
|
// if cast for noMana no Alternative costs are allowed
|
||||||
if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) {
|
if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) {
|
||||||
|
@ -3064,7 +3065,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (availableMana == null) {
|
if (availableMana == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (Mana mana : manaCosts.getOptions()) {
|
|
||||||
|
// alternative cost reduce
|
||||||
|
copyAbility = ability.copy();
|
||||||
|
copyAbility.getManaCostsToPay().clear();
|
||||||
|
copyAbility.getManaCostsToPay().addAll(manaCosts.copy());
|
||||||
|
sourceObject.adjustCosts(copyAbility, game);
|
||||||
|
game.getContinuousEffects().costModification(copyAbility, game);
|
||||||
|
|
||||||
|
for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) {
|
||||||
for (Mana avail : availableMana) {
|
for (Mana avail : availableMana) {
|
||||||
if (mana.enough(avail)) {
|
if (mana.enough(avail)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -3092,7 +3101,18 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (manaCosts.isEmpty()) {
|
if (manaCosts.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
for (Mana mana : manaCosts.getOptions()) {
|
if (availableMana == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternative cost reduce
|
||||||
|
copyAbility = ability.copy();
|
||||||
|
copyAbility.getManaCostsToPay().clear();
|
||||||
|
copyAbility.getManaCostsToPay().addAll(manaCosts.copy());
|
||||||
|
sourceObject.adjustCosts(copyAbility, game);
|
||||||
|
game.getContinuousEffects().costModification(copyAbility, game);
|
||||||
|
|
||||||
|
for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) {
|
||||||
for (Mana avail : availableMana) {
|
for (Mana avail : availableMana) {
|
||||||
if (mana.enough(avail)) {
|
if (mana.enough(avail)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue