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:
Oleg Agafonov 2020-07-05 23:11:47 +04:00
parent f9a9a55f7b
commit 1e744a0aae
12 changed files with 218 additions and 114 deletions

View file

@ -1,16 +1,16 @@
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.constants.SubType;
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 {
@ -18,22 +18,15 @@ public enum ProwlCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
ProwlWatcher watcher = game.getState().getWatcher(ProwlWatcher.class);
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;
}
if (watcher != null && card != null) {
for (SubType subtype : card.getSubtype(game)) {
if (watcher.hasSubtypeMadeCombatDamage(source.getControllerId(), subtype)) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
return "{source}'s prowl cost was paid";
}
}

View file

@ -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";
}
}

View file

@ -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;
}
}

View 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;
}
}

View file

@ -1,30 +1,26 @@
package mage.abilities.keyword;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.costs.AlternativeCost2;
import mage.abilities.costs.AlternativeCost2Impl;
import mage.abilities.costs.AlternativeSourceCosts;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.condition.common.ProwlCondition;
import mage.abilities.costs.*;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.hint.common.ProwlHint;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.watchers.common.ProwlWatcher;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* 702.74. Prowl #
*
* <p>
* 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
* 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;
public ProwlAbility(Card card, String manaString) {
super(Zone.STACK, null);
setRuleAtTheTop(true);
name = PROWL_KEYWORD;
setReminderText(card);
super(Zone.ALL, null);
this.setRuleAtTheTop(true);
this.name = PROWL_KEYWORD;
this.setReminderText(card);
this.addProwlCost(manaString);
addWatcher(new ProwlWatcher());
this.addWatcher(new ProwlWatcher());
this.addHint(ProwlHint.instance);
}
public ProwlAbility(final ProwlAbility ability) {
@ -85,26 +81,17 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
@Override
public boolean isAvailable(Ability source, Game game) {
return true;
return ProwlCondition.instance.apply(game, source);
}
@Override
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
Player player = game.getPlayer(controllerId);
ProwlWatcher prowlWatcher = game.getState().getWatcher(ProwlWatcher.class);
Card card = game.getCard(ability.getSourceId());
if (player == null || prowlWatcher == null || card == null) {
throw new IllegalArgumentException("Params can't be null");
if (player == null) {
return false;
}
boolean canProwl = false;
for (SubType subtype : card.getSubtype(game)) {
if (prowlWatcher.hasSubtypeMadeCombatDamage(ability.getControllerId(), subtype)) {
canProwl = true;
break;
}
}
if (canProwl) {
if (ProwlCondition.instance.apply(game, ability)) {
this.resetProwl();
for (AlternativeCost2 prowlCost : prowlCosts) {
if (prowlCost.canPay(ability, sourceId, controllerId, game)
@ -112,7 +99,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
prowlCost.activate();
ability.getManaCostsToPay().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();
if (cost instanceof ManaCostsImpl) {
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
@ -162,7 +149,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
}
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}";
}
@ -174,4 +161,4 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost
}
return alterCosts;
}
}
}

View file

@ -3046,6 +3046,7 @@ public abstract class PlayerImpl implements Player, Serializable {
protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions availableMana, Ability ability, Game game) {
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
Ability copyAbility; // for alternative cost and reduce tries
for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) {
// if cast for noMana no Alternative costs are allowed
if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) {
@ -3064,7 +3065,15 @@ public abstract class PlayerImpl implements Player, Serializable {
if (availableMana == null) {
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) {
if (mana.enough(avail)) {
return true;
@ -3092,7 +3101,18 @@ public abstract class PlayerImpl implements Player, Serializable {
if (manaCosts.isEmpty()) {
return true;
} 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) {
if (mana.enough(avail)) {
return true;