mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 19:41:59 -08:00
* Angel of Jubilation - Fixed that it did not only prevent life payment from casting spells or activating abilities (fixes #3663).
This commit is contained in:
parent
57de10d609
commit
ffa837ae95
14 changed files with 118 additions and 40 deletions
|
|
@ -74,7 +74,7 @@ class GarzasAssassinCost extends CostImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||||
Player controller = game.getPlayer(controllerId);
|
Player controller = game.getPlayer(controllerId);
|
||||||
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost());
|
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class InfernalDarknessCost extends CostImpl {
|
||||||
|
|
||||||
manaCost.clearPaid();
|
manaCost.clearPaid();
|
||||||
if (manaCost.pay(ability, game, player.getId(), player.getId(), false)
|
if (manaCost.pay(ability, game, player.getId(), player.getId(), false)
|
||||||
&& player.canPayLifeCost()
|
&& player.canPayLifeCost(ability)
|
||||||
&& player.getLife() >= 1
|
&& player.getLife() >= 1
|
||||||
&& lifeCost.pay(ability, game, player.getId(), player.getId(), false)) {
|
&& lifeCost.pay(ability, game, player.getId(), player.getId(), false)) {
|
||||||
paid = true;
|
paid = true;
|
||||||
|
|
@ -91,7 +91,7 @@ class InfernalDarknessCost extends CostImpl {
|
||||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||||
Player player = game.getPlayer(controllerId);
|
Player player = game.getPlayer(controllerId);
|
||||||
if (player != null
|
if (player != null
|
||||||
&& player.canPayLifeCost()
|
&& player.canPayLifeCost(ability)
|
||||||
&& player.getLife() >= 1) {
|
&& player.getLife() >= 1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class LurkingEvilCost extends CostImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||||
Player controller = game.getPlayer(controllerId);
|
Player controller = game.getPlayer(controllerId);
|
||||||
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost());
|
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class MurderousBetrayalCost extends CostImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||||
Player controller = game.getPlayer(controllerId);
|
Player controller = game.getPlayer(controllerId);
|
||||||
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost());
|
return controller != null && (controller.getLife() < 1 || controller.canPayLifeCost(ability));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ class SylvanLibraryEffect extends OneShotEffect {
|
||||||
for (UUID cardId : target.getTargets()) {
|
for (UUID cardId : target.getTargets()) {
|
||||||
Card card = cards.get(cardId, game);
|
Card card = cards.get(cardId, game);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
if (controller.canPayLifeCost()
|
if (controller.canPayLifeCost(source)
|
||||||
&& controller.getLife() >= 4
|
&& controller.getLife() >= 4
|
||||||
&& controller.chooseUse(outcome, "Pay 4 life for " + card.getLogName() + "? (Otherwise it's put on top of your library)", source, game)) {
|
&& controller.chooseUse(outcome, "Pay 4 life for " + card.getLogName() + "? (Otherwise it's put on top of your library)", source, game)) {
|
||||||
controller.loseLife(4, game, false);
|
controller.loseLife(4, game, false);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ class WandOfDenialEffect extends OneShotEffect {
|
||||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||||
controller.lookAtCards(sourceObject != null ? sourceObject.getName() : "", new CardsImpl(card), game);
|
controller.lookAtCards(sourceObject != null ? sourceObject.getName() : "", new CardsImpl(card), game);
|
||||||
if (!card.isLand()
|
if (!card.isLand()
|
||||||
&& controller.canPayLifeCost()
|
&& controller.canPayLifeCost(source)
|
||||||
&& controller.getLife() >= 2
|
&& controller.getLife() >= 2
|
||||||
&& controller.chooseUse(Outcome.Neutral, "Pay 2 life to put " + card.getLogName() + " into graveyard?", source, game)) {
|
&& controller.chooseUse(Outcome.Neutral, "Pay 2 life to put " + card.getLogName() + " into graveyard?", source, game)) {
|
||||||
controller.loseLife(2, game, false);
|
controller.loseLife(2, game, false);
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Player otherPlayer = game.getPlayer(playerId);
|
Player otherPlayer = game.getPlayer(playerId);
|
||||||
if (otherPlayer.canPayLifeCost()
|
if (otherPlayer.canPayLifeCost(source)
|
||||||
&& otherPlayer.getLife() >= 2) {
|
&& otherPlayer.getLife() >= 2) {
|
||||||
PayLifeCost lifeCost = new PayLifeCost(2);
|
PayLifeCost lifeCost = new PayLifeCost(2);
|
||||||
while (otherPlayer.canRespond()
|
while (otherPlayer.canRespond()
|
||||||
|
|
|
||||||
|
|
@ -177,4 +177,71 @@ public class AngelOfJubilationTest extends CardTestPlayerBase {
|
||||||
assertPermanentCount(playerB, "Wasteland", 0);
|
assertPermanentCount(playerB, "Wasteland", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/magefree/mage/issues/3663
|
||||||
|
*
|
||||||
|
* Angel of Jubilation should just prevent paying life for activating
|
||||||
|
* abilities, but currently when it is out the opponent is not prompted to
|
||||||
|
* choose whether or not to pay life for Athreos.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAthreosLifePayNotPrevented() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
// Other nonblack creatures you control get +1/+1.
|
||||||
|
// Players can't pay life or sacrifice creatures to cast spells or activate abilities
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Angel of Jubilation");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
// Indestructible
|
||||||
|
// As long as your devotion to white and black is less than seven, Athreos isn't a creature.
|
||||||
|
// Whenever another creature you own dies, return it to your hand unless target opponent pays 3 life.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Athreos, God of Passage");
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
|
||||||
|
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||||
|
setChoice(playerB, "Yes"); // Pay 3 life to prevent that returns to PlayerA's hand?
|
||||||
|
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||||
|
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 17);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 5/1/2012
|
||||||
|
*
|
||||||
|
* If a spell or activated ability has a cost that requires a player to pay
|
||||||
|
* life (as Griselbrand’s activated ability does) or sacrifice a creature
|
||||||
|
* (as Fling does), that spell or ability can’t be cast or activated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGriselbrandCantPay() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
// Other nonblack creatures you control get +1/+1.
|
||||||
|
// Players can't pay life or sacrifice creatures to cast spells or activate abilities
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Angel of Jubilation");
|
||||||
|
|
||||||
|
// Pay 7 life: Draw seven cards.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Griselbrand");
|
||||||
|
|
||||||
|
checkPlayableAbility("activated ability", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Pay 7 life", false);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3362,8 +3362,8 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPayLifeCost() {
|
public boolean canPayLifeCost(Ability ability) {
|
||||||
return computerPlayer.canPayLifeCost();
|
return computerPlayer.canPayLifeCost(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ public class PlayerStub implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPayLifeCost() {
|
public boolean canPayLifeCost(Ability ability) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class PayLifeCost extends CostImpl {
|
||||||
//life total; in other words, the player loses that much life. (Players can always pay 0 life.)
|
//life total; in other words, the player loses that much life. (Players can always pay 0 life.)
|
||||||
int lifeToPayAmount = amount.calculate(game, ability, null);
|
int lifeToPayAmount = amount.calculate(game, ability, null);
|
||||||
// Paying 0 life is not considered paying any life.
|
// Paying 0 life is not considered paying any life.
|
||||||
if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost()) {
|
if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost(ability)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0;
|
return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class PayVariableLifeCost extends VariableCostImpl {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
// Paying 0 life is not considered paying any life, so paying 0 is still allowed
|
// Paying 0 life is not considered paying any life, so paying 0 is still allowed
|
||||||
if (game.getPlayer(source.getControllerId()).canPayLifeCost()) {
|
if (game.getPlayer(source.getControllerId()).canPayLifeCost(source)) {
|
||||||
maxValue = controller.getLife();
|
maxValue = controller.getLife();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,13 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
|
|
||||||
void setCanPayLifeCost(boolean canPayLifeCost);
|
void setCanPayLifeCost(boolean canPayLifeCost);
|
||||||
|
|
||||||
boolean canPayLifeCost();
|
/**
|
||||||
|
* Can the player pay life for spells or activated abilities
|
||||||
|
*
|
||||||
|
* @param Ability
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean canPayLifeCost(Ability Ability);
|
||||||
|
|
||||||
void setCanPaySacrificeCostFilter(FilterPermanent filter);
|
void setCanPaySacrificeCostFilter(FilterPermanent filter);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
this.inRange.clear();
|
this.inRange.clear();
|
||||||
this.inRange.addAll(player.getInRange());
|
this.inRange.addAll(player.getInRange());
|
||||||
this.canPayLifeCost = player.canPayLifeCost();
|
this.canPayLifeCost = player.canPayLifeCost(null);
|
||||||
this.sacrificeCostFilter = player.getSacrificeCostFilter() != null
|
this.sacrificeCostFilter = player.getSacrificeCostFilter() != null
|
||||||
? player.getSacrificeCostFilter().copy() : null;
|
? player.getSacrificeCostFilter().copy() : null;
|
||||||
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
||||||
|
|
@ -3757,8 +3757,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPayLifeCost() {
|
public boolean canPayLifeCost(Ability ability) {
|
||||||
return isLifeTotalCanChange() && canPayLifeCost;
|
if (!canPayLifeCost
|
||||||
|
&& (AbilityType.ACTIVATED.equals(ability.getAbilityType())
|
||||||
|
|| AbilityType.SPELL.equals(ability.getAbilityType()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isLifeTotalCanChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue