* Monohybrid mana cost improves:

* fixed wrong manually pay by mana pool (it pays generic cost instead colored part of monohybrid);
 * fixed not working cost reduction effects (now monohybrid cost will be reduced correctly with some limitation, see #6130);
This commit is contained in:
Oleg Agafonov 2020-02-11 22:29:07 +04:00
parent 13ad86cb21
commit b5acf64772
9 changed files with 589 additions and 78 deletions

View file

@ -29,7 +29,15 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
*/
void setX(int xValue, int xPay);
void load(String mana);
default void load(String mana) {
load(mana, false);
}
/**
* @param mana mana in strinct like "{2}{R}" or "{2/W}"
* @param extractMonoHybridGenericValue for tests only, extract generic mana value from mono hybrid string
*/
void load(String mana, boolean extractMonoHybridGenericValue);
List<String> getSymbols();

View file

@ -17,9 +17,11 @@ import mage.game.Game;
import mage.players.ManaPool;
import mage.players.Player;
import mage.target.Targets;
import mage.util.CardUtil;
import mage.util.ManaUtil;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @param <T>
@ -30,7 +32,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
protected final UUID id;
protected String text = null;
private static Map<String, ManaCosts> costs = new HashMap<>();
private static Map<String, ManaCosts> costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls
public ManaCostsImpl() {
this.id = UUID.randomUUID();
@ -186,7 +188,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
tempCosts.pay(source, game, source.getSourceId(), player.getId(), false, null);
}
private void handleKrrikPhyrexianManaCosts(UUID payingPlayerId, Ability source, Game game) {
Player player = game.getPlayer(payingPlayerId);
if (this == null || player == null) {
@ -208,20 +210,16 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
/* find which color mana is in the cost and set it in the temp Phyrexian cost */
if (phyrexianColors.isWhite() && mana.getWhite() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.W);
}
else if (phyrexianColors.isBlue() && mana.getBlue() > 0) {
} else if (phyrexianColors.isBlue() && mana.getBlue() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.U);
}
else if (phyrexianColors.isBlack() && mana.getBlack() > 0) {
} else if (phyrexianColors.isBlack() && mana.getBlack() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.B);
}
else if (phyrexianColors.isRed() && mana.getRed() > 0) {
} else if (phyrexianColors.isRed() && mana.getRed() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.R);
}
else if (phyrexianColors.isGreen() && mana.getGreen() > 0) {
} else if (phyrexianColors.isGreen() && mana.getGreen() > 0) {
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.G);
}
if (tempPhyrexianCost != null) {
PayLifeCost payLifeCost = new PayLifeCost(2);
if (payLifeCost.canPay(source, source.getSourceId(), player.getId(), game)
@ -297,18 +295,36 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
public void setPayment(Mana mana) {
}
private boolean canPayColoredManaFromPool(ManaType needColor, ManaCost cost, ManaType canUseManaType, ManaPool pool) {
if (canUseManaType == null || canUseManaType.equals(needColor)) {
return cost.containsColor(CardUtil.manaTypeToColoredManaSymbol(needColor))
&& (pool.getColoredAmount(needColor) > 0 || pool.ConditionalManaHasManaType(needColor));
}
return false;
}
@Override
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
boolean wasUnlockedManaType = (pool.getUnlockedManaType() != null);
if (!pool.isAutoPayment() && !wasUnlockedManaType) {
// if auto payment is inactive and no mana type was clicked manually - do nothing
return;
// try to assign mana from pool to payment in priority order (color first)
// auto-payment allows to use any mana type, if not then only unlocked can be used (mana type that were clicked in mana pool)
ManaType canUseManaType;
if (pool.isAutoPayment()) {
canUseManaType = null; // can use any type
} else {
canUseManaType = pool.getUnlockedManaType();
if (canUseManaType == null) {
// auto payment is inactive and no mana type was clicked manually - do nothing
return;
}
}
ManaCosts referenceCosts = null;
if (pool.isForcedToPay()) {
referenceCosts = this.copy();
}
// attempt to pay colorless costs (not generic) mana costs first
// colorless costs (not generic)
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof ColorlessManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -317,7 +333,8 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
}
//attempt to pay colored costs first
// colored
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof ColoredManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -327,6 +344,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// hybrid
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof HybridManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -336,15 +354,15 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// Mono Hybrid mana costs
// First try only to pay colored mana or conditional colored mana with the pool
// monohybrid
// try to pay colored part
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof MonoHybridManaCost) {
if (((cost.containsColor(ColoredManaSymbol.W)) && (pool.getWhite() > 0 || pool.ConditionalManaHasManaType(ManaType.WHITE)))
|| ((cost.containsColor(ColoredManaSymbol.B)) && (pool.getBlack() > 0 || pool.ConditionalManaHasManaType(ManaType.BLACK)))
|| ((cost.containsColor(ColoredManaSymbol.R)) && (pool.getRed() > 0 || pool.ConditionalManaHasManaType(ManaType.RED)))
|| ((cost.containsColor(ColoredManaSymbol.G)) && (pool.getGreen() > 0 || pool.ConditionalManaHasManaType(ManaType.GREEN)))
|| ((cost.containsColor(ColoredManaSymbol.U)) && (pool.getBlue() > 0) || pool.ConditionalManaHasManaType(ManaType.BLUE))) {
if (canPayColoredManaFromPool(ManaType.WHITE, cost, canUseManaType, pool)
|| canPayColoredManaFromPool(ManaType.BLACK, cost, canUseManaType, pool)
|| canPayColoredManaFromPool(ManaType.RED, cost, canUseManaType, pool)
|| canPayColoredManaFromPool(ManaType.GREEN, cost, canUseManaType, pool)
|| canPayColoredManaFromPool(ManaType.BLUE, cost, canUseManaType, pool)) {
cost.assignPayment(game, ability, pool, costToPay);
if (pool.isEmpty() && pool.getConditionalMana().isEmpty()) {
return;
@ -352,7 +370,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
}
// if colored didn't fit pay colorless with the mana
// try to pay generic part
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof MonoHybridManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -362,6 +380,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// snow
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof SnowManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -371,6 +390,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// generic
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof GenericManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
@ -380,14 +400,16 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
// variable (generic)
for (ManaCost cost : this) {
if (!cost.isPaid() && cost instanceof VariableManaCost) {
cost.assignPayment(game, ability, pool, costToPay);
}
}
// stop using mana of the clicked mana type
pool.lockManaType();
if (!wasUnlockedManaType) {
if (canUseManaType == null) {
handleForcedToPayOnlyForCurrentPayment(game, pool, referenceCosts);
}
}
@ -420,10 +442,10 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
@Override
public final void load(String mana) {
public final void load(String mana, boolean extractMonoHybridGenericValue) {
this.clear();
if (costs.containsKey(mana)) {
ManaCosts<ManaCost> savedCosts = costs.get(mana);
if (!extractMonoHybridGenericValue && mana != null && costsCache.containsKey(mana)) {
ManaCosts<ManaCost> savedCosts = costsCache.get(mana);
for (ManaCost cost : savedCosts) {
this.add(cost.copy());
}
@ -455,7 +477,14 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
this.add(new VariableManaCost(modifierForX));
} //TODO: handle multiple {X} and/or {Y} symbols
} else if (Character.isDigit(symbol.charAt(0))) {
this.add(new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2))));
MonoHybridManaCost cost;
if (extractMonoHybridGenericValue) {
// for tests only, no usage in real game
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)), Integer.parseInt(symbol.substring(0, 1)));
} else {
cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)));
}
this.add(cost);
} else if (symbol.contains("P")) {
this.add(new PhyrexianManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
} else {
@ -463,7 +492,9 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
}
costs.put(mana, this.copy());
if (!extractMonoHybridGenericValue) {
costsCache.put(mana, this.copy());
}
}
}

View file

@ -12,46 +12,53 @@ import java.util.List;
public class MonoHybridManaCost extends ManaCostImpl {
private final ColoredManaSymbol mana;
private int mana2 = 2;
private final ColoredManaSymbol manaColor;
private int manaGeneric;
public MonoHybridManaCost(ColoredManaSymbol mana) {
this.mana = mana;
this.cost = new Mana(mana);
this.cost.add(Mana.GenericMana(2));
addColoredOption(mana);
options.add(Mana.GenericMana(2));
public MonoHybridManaCost(ColoredManaSymbol manaColor) {
this(manaColor, 2);
}
public MonoHybridManaCost(ColoredManaSymbol manaColor, int genericAmount) {
this.manaColor = manaColor;
this.manaGeneric = genericAmount;
this.cost = new Mana(manaColor);
this.cost.add(Mana.GenericMana(genericAmount));
addColoredOption(manaColor);
options.add(Mana.GenericMana(genericAmount));
}
public MonoHybridManaCost(MonoHybridManaCost manaCost) {
super(manaCost);
this.mana = manaCost.mana;
this.mana2 = manaCost.mana2;
this.manaColor = manaCost.manaColor;
this.manaGeneric = manaCost.manaGeneric;
}
@Override
public int convertedManaCost() {
return 2;
// from wiki: A card with monocolored hybrid mana symbols in its mana cost has a converted mana cost equal to
// the highest possible cost it could be played for. Its converted mana cost never changes.
return Math.max(manaGeneric, 1);
}
@Override
public boolean isPaid() {
if (paid || isColoredPaid(this.mana)) {
if (paid || isColoredPaid(this.manaColor)) {
return true;
}
return isColorlessPaid(this.mana2);
return isColorlessPaid(this.manaGeneric);
}
@Override
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
if (!assignColored(ability, game, pool, mana, costToPay)) {
assignGeneric(ability, game, pool, mana2, null, costToPay);
if (!assignColored(ability, game, pool, manaColor, costToPay)) {
assignGeneric(ability, game, pool, manaGeneric, null, costToPay);
}
}
@Override
public String getText() {
return "{2/" + mana.toString() + '}';
return "{" + manaGeneric + "/" + manaColor.toString() + '}';
}
@Override
@ -61,7 +68,7 @@ public class MonoHybridManaCost extends ManaCostImpl {
@Override
public boolean testPay(Mana testMana) {
switch (mana) {
switch (manaColor) {
case B:
if (testMana.getBlack() > 0) {
return true;
@ -93,18 +100,18 @@ public class MonoHybridManaCost extends ManaCostImpl {
@Override
public boolean containsColor(ColoredManaSymbol coloredManaSymbol) {
return mana == coloredManaSymbol;
return manaColor == coloredManaSymbol;
}
public ColoredManaSymbol getManaColor() {
return mana;
return manaColor;
}
@Override
public List<Mana> getManaOptions() {
List<Mana> manaList = new ArrayList<>();
manaList.add(new Mana(mana));
manaList.add(Mana.GenericMana(2));
manaList.add(new Mana(manaColor));
manaList.add(Mana.GenericMana(manaGeneric));
return manaList;
}
}