[DMU] Karn's Sylex (#9507)

This commit is contained in:
Alex Vasile 2022-09-17 22:37:56 -04:00 committed by GitHub
parent adbebfdd3e
commit b14af42280
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 285 additions and 30 deletions

View file

@ -8,12 +8,14 @@ import mage.abilities.costs.Costs;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.mana.ManaAbility;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.constants.*;
import mage.game.Game;
import mage.game.command.CommandObject;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
@ -204,7 +206,9 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
return getManaCostsToPay().getOptions();
Player player = game.getPlayer(playerId);
return getManaCostsToPay().getOptions(player.canPayLifeCost(this));
}
protected boolean controlsAbility(UUID playerId, Game game) {

View file

@ -31,6 +31,21 @@ public interface ManaCost extends Cost {
ManaOptions getOptions();
/**
* Return all options for paying the mana cost (this) while taking into accoutn if the player can pay life.
* Used to correctly highlight (or not) spells with Phyrexian mana depending on if the player can pay life costs.
* <p>
* E.g. Tezzeret's Gambit has a cost of {3}{U/P}.
* If `canPayLifeCost == true` the two options are {3}{U} and {3}. The second including a 2 life cost that is not
* captured by the ManaOptions object being returned.
* However, if `canPayLifeCost == false` than the return is only {3}{U} since the Phyrexian mana MUST be paid with
* a {U} and not with 2 life.
*
* @param canPayLifeCost if the player is able to pay life for the ability/effect that this cost is associated with
* @return
*/
ManaOptions getOptions(boolean canPayLifeCost);
boolean testPay(Mana testMana);
Filter getSourceFilter();

View file

@ -69,7 +69,18 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
@Override
public ManaOptions getOptions() {
return options;
return getOptions(true);
}
@Override
public ManaOptions getOptions(boolean canPayLifeCost) {
if (!canPayLifeCost && this.isPhyrexian()) {
ManaOptions optionsFiltered = new ManaOptions();
optionsFiltered.add(this.cost);
return optionsFiltered;
} else {
return options;
}
}
@Override

View file

@ -542,9 +542,14 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
@Override
public ManaOptions getOptions() {
return getOptions(true);
}
@Override
public ManaOptions getOptions(boolean canPayLifeCost) {
ManaOptions options = new ManaOptions();
for (ManaCost cost : this) {
options.addMana(cost.getOptions());
options.addMana(cost.getOptions(canPayLifeCost));
}
return options;
}

View file

@ -47,6 +47,22 @@ import java.util.*;
*/
public interface Player extends MageItem, Copyable<Player> {
/**
* Enum used to indicate what each player is allowed to spend life on.
* By default it is set to `allAbilities`, but can be changed by effects.
* E.g. Angel of Jubilation sets it to `nonSpellnonActivatedAbilities`,
* and Karn's Sylex sets it to `onlyManaAbilities`.
*
*
* Default is PayLifeCostLevel.allAbilities.
*/
enum PayLifeCostLevel {
allAbilities,
nonSpellnonActivatedAbilities,
onlyManaAbilities,
none
}
/**
* Current player is real life player (human). Try to use in GUI and network engine only.
* <p>
@ -147,12 +163,12 @@ public interface Player extends MageItem, Copyable<Player> {
/**
* Is the player allowed to pay life for casting spells or activate activated abilities
*
* @param canPayLifeCost
* @param payLifeCostLevel
*/
void setCanPayLifeCost(boolean canPayLifeCost);
void setPayLifeCostLevel(PayLifeCostLevel payLifeCostLevel);
boolean getCanPayLifeCost();
PayLifeCostLevel getPayLifeCostLevel();
/**
* Can the player pay life to cast or activate the given ability

View file

@ -135,7 +135,7 @@ public abstract class PlayerImpl implements Player, Serializable {
protected boolean isTestMode = false;
protected boolean canGainLife = true;
protected boolean canLoseLife = true;
protected boolean canPayLifeCost = true;
protected PayLifeCostLevel payLifeCostLevel = PayLifeCostLevel.allAbilities;
protected boolean loseByZeroOrLessLife = true;
protected boolean canPlayCardsFromGraveyard = true;
protected boolean drawsOnOpponentsTurn = false;
@ -248,7 +248,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.userData = player.userData;
this.matchPlayer = player.matchPlayer;
this.canPayLifeCost = player.canPayLifeCost;
this.payLifeCostLevel = player.payLifeCostLevel;
this.sacrificeCostFilter = player.sacrificeCostFilter;
this.alternativeSourceCosts.addAll(player.alternativeSourceCosts);
this.storedBookmark = player.storedBookmark;
@ -344,7 +344,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.inRange.clear();
this.inRange.addAll(player.getInRange());
this.canPayLifeCost = player.getCanPayLifeCost();
this.payLifeCostLevel = player.getPayLifeCostLevel();
this.sacrificeCostFilter = player.getSacrificeCostFilter() != null
? player.getSacrificeCostFilter().copy() : null;
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
@ -469,7 +469,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.maxAttackedBy = Integer.MAX_VALUE;
this.canGainLife = true;
this.canLoseLife = true;
this.canPayLifeCost = true;
this.payLifeCostLevel = PayLifeCostLevel.allAbilities;
this.sacrificeCostFilter = null;
this.loseByZeroOrLessLife = true;
this.canPlayCardsFromGraveyard = false;
@ -4348,22 +4348,32 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean canPayLifeCost(Ability ability) {
if (!canPayLifeCost
&& (AbilityType.ACTIVATED.equals(ability.getAbilityType())
|| AbilityType.SPELL.equals(ability.getAbilityType()))) {
if (!isLifeTotalCanChange()) {
return false;
}
return isLifeTotalCanChange();
switch (payLifeCostLevel) {
case allAbilities:
return true;
case onlyManaAbilities:
return ability.getAbilityType() == AbilityType.MANA;
case nonSpellnonActivatedAbilities:
return ability.getAbilityType() != AbilityType.ACTIVATED && ability.getAbilityType() != AbilityType.SPELL;
case none:
default:
return false;
}
}
@Override
public boolean getCanPayLifeCost() {
return canPayLifeCost;
public PayLifeCostLevel getPayLifeCostLevel() {
return payLifeCostLevel;
}
@Override
public void setCanPayLifeCost(boolean canPayLifeCost) {
this.canPayLifeCost = canPayLifeCost;
public void setPayLifeCostLevel(PayLifeCostLevel payLifeCostLevel) {
this.payLifeCostLevel = payLifeCostLevel;
}
@Override