* Performance: memory usage optimization for deck editor (part 2 of removed bloated usage of ManaCosts -> ManaColor objects, see #7515);

This commit is contained in:
Oleg Agafonov 2021-02-12 22:12:18 +04:00
parent c1dea5b21e
commit 10e557b873
25 changed files with 164 additions and 113 deletions

View file

@ -178,7 +178,7 @@ public class DeckGeneratorPool
* @return if all the mana symbols fit the chosen colors.
*/
private boolean cardFitsChosenColors(Card card) {
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
boolean found = false;
symbol = symbol.replace("{", "").replace("}", "");
if (isColoredManaSymbol(symbol)) {
@ -218,7 +218,7 @@ public class DeckGeneratorPool
List<Card> fixedSpells = getFixedSpells();
for(Card spell: fixedSpells) {
for (String symbol : spell.getManaCost().getSymbols()) {
for (String symbol : spell.getManaCostSymbols()) {
symbol = symbol.replace("{", "").replace("}", "");
if (isColoredManaSymbol(symbol)) {
for (ColoredManaSymbol allowed : allowedColors) {

View file

@ -134,7 +134,7 @@ public final class DeckBuilder {
final Map<String, Integer> colorCount = new HashMap<>();
for (final Card card : deck.getCards()) {
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
int count = 0;
symbol = symbol.replace("{", "").replace("}", "");
if (isColoredMana(symbol)) {
@ -243,7 +243,7 @@ public final class DeckBuilder {
int maxSingleCount = 0;
int multicolor = 0;
Set<String> colors = new HashSet<>();
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
int count = 0;
symbol = symbol.replace("{", "").replace("}", "");
if (isColoredMana(symbol)) {

View file

@ -31,7 +31,7 @@ public class AbilityView extends CardView {
this.subTypes = new SubTypes();
this.superTypes = EnumSet.noneOf(SuperType.class);
this.color = new ObjectColor();
this.manaCostLeftStr = String.join("", ability.getManaCosts().getSymbols());
this.manaCostLeftStr = String.join("", ability.getManaCostSymbols());
this.manaCostRightStr = "";
}

View file

@ -347,29 +347,29 @@ public class CardView extends SimpleCardView {
if (splitCard != null) {
this.isSplitCard = true;
leftSplitName = splitCard.getLeftHalfCard().getName();
leftSplitCostsStr = String.join("", splitCard.getLeftHalfCard().getManaCost().getSymbols());
leftSplitCostsStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols());
leftSplitRules = splitCard.getLeftHalfCard().getRules(game);
leftSplitTypeLine = getCardTypeLine(game, splitCard.getLeftHalfCard());
rightSplitName = splitCard.getRightHalfCard().getName();
rightSplitCostsStr = String.join("", splitCard.getRightHalfCard().getManaCost().getSymbols());
rightSplitCostsStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols());
rightSplitRules = splitCard.getRightHalfCard().getRules(game);
rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard());
fullCardName = card.getName(); // split card contains full name as normal
this.manaCostLeftStr = String.join("", splitCard.getLeftHalfCard().getManaCost().getSymbols());
this.manaCostRightStr = String.join("", splitCard.getRightHalfCard().getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", splitCard.getLeftHalfCard().getManaCostSymbols());
this.manaCostRightStr = String.join("", splitCard.getRightHalfCard().getManaCostSymbols());
} else if (card instanceof ModalDoubleFacesCard) {
this.isModalDoubleFacesCard = true;
ModalDoubleFacesCard mainCard = ((ModalDoubleFacesCard) card);
fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName();
this.manaCostLeftStr = String.join("", mainCard.getLeftHalfCard().getManaCost().getSymbols());
this.manaCostRightStr = String.join("", mainCard.getRightHalfCard().getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", mainCard.getLeftHalfCard().getManaCostSymbols());
this.manaCostRightStr = String.join("", mainCard.getRightHalfCard().getManaCostSymbols());
} else if (card instanceof AdventureCard) {
AdventureCard adventureCard = ((AdventureCard) card);
AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard());
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
this.manaCostLeftStr = String.join("", adventureCardSpell.getManaCost().getSymbols());
this.manaCostRightStr = String.join("", adventureCard.getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", adventureCardSpell.getManaCostSymbols());
this.manaCostRightStr = String.join("", adventureCard.getManaCostSymbols());
} else if (card instanceof MockCard) {
// deck editor cards
fullCardName = ((MockCard) card).getFullName(true);
@ -377,7 +377,7 @@ public class CardView extends SimpleCardView {
this.manaCostRightStr = String.join("", ((MockCard) card).getManaCostStr(CardInfo.ManaCostSide.RIGHT));
} else {
fullCardName = card.getName();
this.manaCostLeftStr = String.join("", card.getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", card.getManaCostSymbols());
this.manaCostRightStr = "";
}
@ -389,7 +389,7 @@ public class CardView extends SimpleCardView {
} else {
this.rules = card.getRules(game);
}
this.convertedManaCost = card.getManaCost().convertedManaCost();
this.convertedManaCost = card.getConvertedManaCost();
if (card instanceof Permanent) {
this.mageObjectType = MageObjectType.PERMANENT;
@ -563,7 +563,7 @@ public class CardView extends SimpleCardView {
this.subTypes = object.getSubtype(game);
this.superTypes = object.getSuperType();
this.color = object.getColor(game);
this.manaCostLeftStr = String.join("", object.getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", object.getManaCostSymbols());
this.manaCostRightStr = "";
this.convertedManaCost = object.getManaCost().convertedManaCost();
if (object instanceof PermanentToken) {
@ -733,7 +733,7 @@ public class CardView extends SimpleCardView {
this.color = token.getColor(game);
this.frameColor = token.getFrameColor(game);
this.frameStyle = token.getFrameStyle();
this.manaCostLeftStr = String.join("", token.getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", token.getManaCostSymbols());
this.manaCostRightStr = "";
this.rarity = Rarity.SPECIAL;
this.type = token.getTokenType();

View file

@ -44,7 +44,7 @@ public class StackAbilityView extends CardView {
this.subTypes = ability.getSubtype(game);
this.superTypes = ability.getSuperType();
this.color = ability.getColor(game);
this.manaCostLeftStr = String.join("", ability.getManaCost().getSymbols());
this.manaCostLeftStr = String.join("", ability.getManaCostSymbols());
this.manaCostRightStr = "";
this.cardTypes = ability.getCardType();
this.subTypes = ability.getSubtype(game);

View file

@ -2316,7 +2316,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (differentColorsInCost > 0 && differentColorsInCost < 3) {
// if some colors were already chosen, total amount shouldn't be more than 3
if (chosenSymbols.size() + differentColorsInCost < 4) {
for (String symbol : picked.card.getManaCost().getSymbols()) {
for (String symbol : picked.card.getManaCostSymbols()) {
symbol = symbol.replace("{", "").replace("}", "");
if (RateCard.isColoredMana(symbol)) {
chosenSymbols.add(symbol);

View file

@ -68,7 +68,7 @@ enum EmbodimentOfAgoniesValue implements DynamicValue {
.getCards(game)
.stream()
.filter(card -> !card.isLand())
.forEach(card -> stringSet.add(getCosts(card.getManaCost())));
.forEach(card -> stringSet.add(getCosts(card.getManaCostSymbols())));
stringSet.removeIf(s -> s == null || s.equals(""));
return stringSet.size();
}
@ -83,11 +83,11 @@ enum EmbodimentOfAgoniesValue implements DynamicValue {
return "";
}
private static String getCosts(ManaCosts<ManaCost> costs) {
private static String getCosts(List<String> manaCostSymbols) {
List<String> newList = new ArrayList();
int generic = 0;
boolean hasGeneric = false;
for (String s : costs.getSymbols()) {
for (String s : manaCostSymbols) {
if (s.matches("\\{\\d*\\}")) {
generic += Integer.parseInt(s.substring(1, s.length() - 1));
hasGeneric = true;

View file

@ -69,8 +69,7 @@ enum JeganthaTheWellspringCompanionCondition implements CompanionCondition {
private static boolean checkCard(Card card) {
Map<String, Integer> symbolMap = new HashMap();
return card.getManaCost()
.getSymbols()
return card.getManaCostSymbols()
.stream()
.anyMatch(s -> symbolMap.compute(
s, (str, i) -> (i == null) ? 1 : i + 1

View file

@ -37,6 +37,8 @@ public class CostReduceTest extends CardTestPlayerBase {
Assert.assertEquals("test mono hybrid have variant generic", "{1/R}", testCost.getText());
testReduce("{5/R}", 0, "{5/R}"); // ensure that mono hybrid in test mode
// mana order must be same (e.g. cost {R}{2}{G} must be reduced to {R}{1}{G})
// DECREASE COST
// colorless is not reduce
@ -73,16 +75,29 @@ public class CostReduceTest extends CardTestPlayerBase {
testReduce("{R}{3}{G}", 2, "{R}{1}{G}");
testReduce("{R}{G}{3}", 2, "{R}{G}{1}");
// multi generics, decrease cost by 2 (you can't get multigeneric in real game example)
testReduce("{2}{2}", 2, "{2}");
testReduce("{3}{3}", 2, "{1}{3}");
testReduce("{3}{R}{3}", 2, "{1}{R}{3}");
testReduce("{3}{R}{3}{G}", 2, "{1}{R}{3}{G}");
testReduce("{R}{3}{G}{3}", 2, "{R}{1}{G}{3}");
//
testReduce("{2}{2}", 3, "{1}");
testReduce("{3}{3}", 3, "{3}");
testReduce("{3}{R}{3}", 5, "{R}{1}");
testReduce("{3}{R}{3}{G}", 5, "{R}{1}{G}");
testReduce("{R}{3}{G}{3}", 5, "{R}{G}{1}");
// INCREASE COST
// colorless, increase cost by 1
testReduce("{C}", -1, "{C}{1}");
testReduce("{C}{G}", -1, "{C}{G}{1}");
testReduce("{C}", -1, "{1}{C}");
testReduce("{C}{G}", -1, "{1}{C}{G}");
// 0 generic, increase cost by 1
testReduce("", -1, "{1}");
testReduce("{R}", -1, "{R}{1}");
testReduce("{R}{G}", -1, "{R}{G}{1}");
testReduce("{R}", -1, "{1}{R}");
testReduce("{R}{G}", -1, "{1}{R}{G}");
// 1 generic, increase cost by 1
testReduce("{1}", -1, "{2}");
@ -123,8 +138,8 @@ public class CostReduceTest extends CardTestPlayerBase {
testReduce("{2/R}", 1, "{1/R}");
testReduce("{2/R}{2/G}", 1, "{1/R}{2/G}"); // TODO: add or/or reduction? (see https://github.com/magefree/mage/issues/6130 )
// mono hybrid, increase cost by 1
testReduce("{2/R}", -1, "{2/R}{1}");
testReduce("{2/R}{2/G}", -1, "{2/R}{2/G}{1}");
testReduce("{2/R}", -1, "{1}{2/R}");
testReduce("{2/R}{2/G}", -1, "{1}{2/R}{2/G}");
// generic, normal amount
// mono hybrid + 1 generic, decrease cost by 1

View file

@ -1621,7 +1621,7 @@ public class VerifyCardDataTest {
}
String expected = ref.manaCost;
String cost = String.join("", card.getManaCost().getSymbols());
String cost = String.join("", card.getManaCostSymbols());
if (cost.isEmpty()) {
cost = null;
}

View file

@ -88,6 +88,14 @@ public interface MageObject extends MageItem, Serializable {
ManaCosts<ManaCost> getManaCost();
default List<String> getManaCostSymbols() {
List<String> symbols = new ArrayList<>();
for (ManaCost cost : getManaCost()) {
symbols.add(cost.getText());
}
return symbols;
}
int getConvertedManaCost();
MageInt getPower();

View file

@ -22,6 +22,7 @@ import mage.target.targetadjustment.TargetAdjuster;
import mage.watchers.Watcher;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageIdentifier;
@ -123,6 +124,14 @@ public interface Ability extends Controllable, Serializable {
*/
ManaCosts<ManaCost> getManaCosts();
default List<String> getManaCostSymbols() {
List<String> symbols = new ArrayList<>();
for (ManaCost cost : getManaCosts()) {
symbols.add(cost.getText());
}
return symbols;
}
/**
* Gets all the {@link ManaCosts} that must be paid before activating this
* ability. These costs should be modified by any modification effects

View file

@ -39,8 +39,6 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
*/
void load(String mana, boolean extractMonoHybridGenericValue);
List<String> getSymbols();
boolean payOrRollback(Ability ability, Game game, Ability source, UUID payingPlayerId);
@Override

View file

@ -505,15 +505,6 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
}
@Override
public List<String> getSymbols() {
List<String> symbols = new ArrayList<>();
for (ManaCost cost : this) {
symbols.add(cost.getText());
}
return symbols;
}
@Override
public UUID getId() {
return this.id;

View file

@ -43,9 +43,7 @@ public class SpellCostReductionForEachSourceEffect extends CostModificationEffec
StringBuilder sb = new StringBuilder();
sb.append("this spell costs ");
for (String manaSymbol : reduceManaCosts.getSymbols()) {
sb.append(manaSymbol);
}
sb.append(reduceManaCosts.getText());
sb.append(" less to cast for each ").append(this.eachAmount.getMessage());
this.staticText = sb.toString();
}

View file

@ -34,9 +34,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
StringBuilder sb = new StringBuilder();
sb.append("this spell costs ");
for (String manaSymbol : manaCostsToReduce.getSymbols()) {
sb.append(manaSymbol);
}
sb.append(manaCostsToReduce.getText());
sb.append(" less to cast");
if (this.condition != null) {
sb.append(" if ").append(this.condition.toString());

View file

@ -63,9 +63,7 @@ public class SpellsCostIncreasingAllEffect extends CostModificationEffectImpl {
sb.append(" cost ");
if (this.increaseManaCosts != null) {
for (String manaSymbol : this.increaseManaCosts.getSymbols()) {
sb.append(manaSymbol);
}
sb.append(this.increaseManaCosts.getText());
} else {
sb.append("{").append(increaseGenericCost).append("}");
}

View file

@ -37,9 +37,7 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI
StringBuilder sb = new StringBuilder();
sb.append(filter.getMessage()).append(" you cast cost ");
for (String manaSymbol : manaCostsToReduce.getSymbols()) {
sb.append(manaSymbol);
}
sb.append(manaCostsToReduce.getText());
sb.append(" less to cast. This effect reduces only the amount of colored mana you pay.");
this.staticText = sb.toString();
}

View file

@ -4,7 +4,6 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.cards.CardImpl;
import mage.cards.ModalDoubleFacesCard;
import mage.cards.repository.CardInfo;
@ -35,6 +34,7 @@ public class MockCard extends CardImpl {
protected List<String> manaCostStr;
protected String adventureSpellName;
protected boolean isModalDoubleFacesCard;
protected int convertedManaCost;
public MockCard(CardInfo card) {
super(null, card.getName());
@ -49,10 +49,11 @@ public class MockCard extends CardImpl {
this.usesVariousArt = card.usesVariousArt();
this.manaCost = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.ALL)));
//this.manaCost = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.ALL)));
this.manaCostLeftStr = card.getManaCosts(CardInfo.ManaCostSide.LEFT);
this.manaCostRightStr = card.getManaCosts(CardInfo.ManaCostSide.RIGHT);
this.manaCostStr = card.getManaCosts(CardInfo.ManaCostSide.ALL);
this.convertedManaCost = card.getConvertedManaCost();
this.color = card.getColor();
@ -112,21 +113,20 @@ public class MockCard extends CardImpl {
@Override
public ManaCosts<ManaCost> getManaCost() {
return manaCost;
// only split half cards can store mana cost in objects list instead strings (memory optimization)
// see https://github.com/magefree/mage/issues/7515
throw new IllegalArgumentException("Unsupport method call: getManaCost in " + this.getClass().getCanonicalName());
}
/*
private ManaCosts<ManaCost> getManaCost(CardInfo.ManaCostSide manaCostSide) {
switch (manaCostSide) {
case LEFT:
return manaCostLeft;
case RIGHT:
return manaCostRight;
default:
case ALL:
return manaCost;
}
}*/
@Override
public List<String> getManaCostSymbols() {
return getManaCostStr(CardInfo.ManaCostSide.ALL);
}
@Override
public int getConvertedManaCost() {
return this.convertedManaCost;
}
public List<String> getManaCostStr(CardInfo.ManaCostSide manaCostSide) {
switch (manaCostSide) {
@ -164,14 +164,6 @@ public class MockCard extends CardImpl {
}
}
private String join(List<String> strings) {
StringBuilder sb = new StringBuilder();
for (String string : strings) {
sb.append(string);
}
return sb.toString();
}
private Ability textAbilityFromString(final String text) {
return new MockAbility(text);
}

View file

@ -1,10 +1,16 @@
package mage.cards.mock;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.cards.Card;
import mage.cards.SplitCard;
import mage.cards.SplitCardHalf;
import mage.cards.repository.CardInfo;
import java.util.List;
/**
*
* @author LevelX2
@ -12,9 +18,13 @@ import mage.cards.repository.CardInfo;
public class MockSplitCardHalf extends MockCard implements SplitCardHalf {
private SplitCard splitCardParent;
private ManaCosts<ManaCost> manaCosts;
private List<String> manaCostsSymbols;
public MockSplitCardHalf(CardInfo card) {
super(card);
this.manaCostsSymbols = card.getManaCosts(CardInfo.ManaCostSide.ALL);
this.manaCosts = new ManaCostsImpl<>(String.join("", this.manaCostsSymbols));
}
public MockSplitCardHalf(final MockSplitCardHalf card) {
@ -36,4 +46,15 @@ public class MockSplitCardHalf extends MockCard implements SplitCardHalf {
return splitCardParent;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
// only split half cards can store mana cost in objects list instead strings (memory optimization)
return manaCosts;
}
@Override
public List<String> getManaCostSymbols() {
// only split half cards can store mana cost in objects list instead strings (memory optimization)
return manaCostsSymbols;
}
}

View file

@ -167,19 +167,19 @@ public class CardInfo {
// mana cost can contains multiple cards (split left/right, modal double faces, card/adventure)
if (card instanceof SplitCard) {
List<String> manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols();
List<String> manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols();
List<String> manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCostSymbols();
List<String> manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCostSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else if (card instanceof ModalDoubleFacesCard) {
List<String> manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols();
List<String> manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols();
List<String> manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCostSymbols();
List<String> manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCostSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else if (card instanceof AdventureCard) {
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols();
List<String> manaCostRight = card.getManaCost().getSymbols();
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCostSymbols();
List<String> manaCostRight = card.getManaCostSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else {
this.setManaCosts(card.getManaCost().getSymbols());
this.setManaCosts(card.getManaCostSymbols());
}
int length = 0;

View file

@ -317,10 +317,10 @@ public final class RateCard {
* @return
*/
private static int getManaCostScore(Card card, List<ColoredManaSymbol> allowedColors) {
int converted = card.getManaCost().convertedManaCost();
int converted = card.getConvertedManaCost();
if (allowedColors == null) {
int colorPenalty = 0;
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
if (isColoredMana(symbol)) {
colorPenalty++;
}
@ -329,7 +329,7 @@ public final class RateCard {
}
final Map<String, Integer> singleCount = new HashMap<>();
int maxSingleCount = 0;
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
int count = 0;
symbol = symbol.replace("{", "").replace("}", "");
if (isColoredMana(symbol)) {
@ -385,7 +385,7 @@ public final class RateCard {
*/
public static int getColorManaCount(Card card) {
int count = 0;
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
if (isColoredMana(symbol)) {
count++;
}
@ -401,7 +401,7 @@ public final class RateCard {
*/
public static int getDifferentColorManaCount(Card card) {
Set<String> symbols = new HashSet<>();
for (String symbol : card.getManaCost().getSymbols()) {
for (String symbol : card.getManaCostSymbols()) {
if (isColoredMana(symbol)) {
symbols.add(symbol);
}

View file

@ -1,9 +1,6 @@
package mage.game.stack;
import mage.MageIdentifier;
import mage.MageInt;
import mage.MageObject;
import mage.ObjectColor;
import mage.*;
import mage.abilities.*;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostAdjuster;
@ -222,6 +219,11 @@ public class StackAbility extends StackObjImpl implements Ability {
return emptyCost;
}
@Override
public List<String> getManaCostSymbols() {
return super.getManaCostSymbols();
}
@Override
public MageInt getPower() {
return MageInt.EmptyMageInt;

View file

@ -133,29 +133,37 @@ public final class CardUtil {
}
private static ManaCosts<ManaCost> adjustCost(ManaCosts<ManaCost> manaCosts, int reduceCount) {
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
ManaCosts<ManaCost> newCost = new ManaCostsImpl<>();
// nothing to change
if (reduceCount == 0) {
for (ManaCost manaCost : manaCosts) {
adjustedCost.add(manaCost.copy());
newCost.add(manaCost.copy());
}
return adjustedCost;
return newCost;
}
// keep same order for costs
Map<ManaCost, ManaCost> changedCost = new LinkedHashMap<>(); // must be ordered
List<ManaCost> addedCost = new ArrayList<>();
manaCosts.forEach(manaCost -> {
changedCost.put(manaCost, manaCost);
});
// remove or save cost
if (reduceCount > 0) {
int restToReduce = reduceCount;
// first run - priority for single option costs (generic)
for (ManaCost manaCost : manaCosts) {
// ignore snow mana
if (manaCost instanceof SnowManaCost) {
adjustedCost.add(manaCost);
continue;
}
// ignore unknown mana
if (manaCost.getOptions().size() == 0) {
adjustedCost.add(manaCost);
continue;
}
@ -171,15 +179,15 @@ public final class CardUtil {
if ((colorless - restToReduce) > 0) {
// partly reduce
int newColorless = colorless - restToReduce;
adjustedCost.add(new GenericManaCost(newColorless));
changedCost.put(manaCost, new GenericManaCost(newColorless));
restToReduce = 0;
} else {
// full reduce - ignore cost
changedCost.put(manaCost, null);
restToReduce -= colorless;
}
} else {
// nothing to reduce
adjustedCost.add(manaCost.copy());
}
}
@ -203,22 +211,21 @@ public final class CardUtil {
if ((colorless - restToReduce) > 0) {
// partly reduce
int newColorless = colorless - restToReduce;
adjustedCost.add(new MonoHybridManaCost(mono.getManaColor(), newColorless));
changedCost.put(manaCost, new MonoHybridManaCost(mono.getManaColor(), newColorless));
restToReduce = 0;
} else {
// full reduce
adjustedCost.add(new MonoHybridManaCost(mono.getManaColor(), 0));
changedCost.put(manaCost, new MonoHybridManaCost(mono.getManaColor(), 0));
restToReduce -= colorless;
}
} else {
// nothing to reduce
adjustedCost.add(mono.copy());
}
continue;
}
// unsupported multi-option mana types for reduce (like HybridManaCost)
adjustedCost.add(manaCost.copy());
// nothing to do
}
}
@ -226,23 +233,39 @@ public final class CardUtil {
if (reduceCount < 0) {
boolean added = false;
for (ManaCost manaCost : manaCosts) {
// ignore already reduced cost (add new cost to the start)
if (changedCost.get(manaCost) == null) {
continue;
}
// add to existing cost
if (reduceCount != 0 && manaCost instanceof GenericManaCost) {
// add increase cost to existing generic
GenericManaCost gen = (GenericManaCost) manaCost;
adjustedCost.add(new GenericManaCost(gen.getOptions().get(0).getGeneric() + -reduceCount));
changedCost.put(manaCost, new GenericManaCost(gen.getOptions().get(0).getGeneric() + -reduceCount));
reduceCount = 0;
added = true;
} else {
// non-generic mana
adjustedCost.add(manaCost.copy());
}
}
// add as new cost
if (!added) {
// add increase cost as new
adjustedCost.add(new GenericManaCost(-reduceCount));
addedCost.add(new GenericManaCost(-reduceCount));
}
}
// collect final result
addedCost.forEach(cost -> {
newCost.add(cost.copy());
});
changedCost.forEach((key, value) -> {
// ignore fully reduced and add changed
if (value != null) {
newCost.add(value.copy());
}
});
// cost modifying effects requiring snow mana unnecessarily (fixes #6000)
Filter filter = manaCosts.stream()
.filter(manaCost -> !(manaCost instanceof SnowManaCost))
@ -251,9 +274,10 @@ public final class CardUtil {
.findFirst()
.orElse(null);
if (filter != null) {
adjustedCost.setSourceFilter(filter);
newCost.setSourceFilter(filter);
}
return adjustedCost;
return newCost;
}
public static void reduceCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToReduce) {

View file

@ -576,7 +576,7 @@ public final class ManaUtil {
res.setWhite(res.isWhite() || secondColor.isWhite());
// from mana
List<String> secondManaSymbols = secondSideCard.getManaCost().getSymbols();
List<String> secondManaSymbols = secondSideCard.getManaCostSymbols();
res.setWhite(res.isWhite() || containsManaSymbol(secondManaSymbols, "W"));
res.setBlue(res.isBlue() || containsManaSymbol(secondManaSymbols, "U"));
res.setBlack(res.isBlack() || containsManaSymbol(secondManaSymbols, "B"));
@ -628,7 +628,7 @@ public final class ManaUtil {
} else {
secondSide = card.getSecondCardFace();
}
return getColorIdentity(card.getColor(), String.join("", card.getManaCost().getSymbols()), card.getRules(), secondSide);
return getColorIdentity(card.getColor(), String.join("", card.getManaCostSymbols()), card.getRules(), secondSide);
}
public static int getColorIdentityHash(FilterMana colorIdentity) {