Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions forge-core/src/main/java/forge/card/mana/ManaCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,18 @@ private void sealClass(List<ManaCostShard> shards0) {
*/
public ManaCost(final IParserManaCost parser) {
final List<ManaCostShard> shardsTemp = Lists.newArrayList();
boolean xMana = false;
while (parser.hasNext()) {
final ManaCostShard shard = parser.next();
if (shard != null && shard != ManaCostShard.GENERIC) {
if (shard.toString().equals("{X}")) {
xMana = true;
}
shardsTemp.add(shard);
} // null is OK - that was generic mana
}
int generic = parser.getTotalGenericCost(); // collect generic mana here
this.hasNoCost = generic == -1;
this.hasNoCost = !xMana && generic == -1;
this.genericCost = hasNoCost ? 0 : generic;
sealClass(shardsTemp);
}
Expand All @@ -117,11 +121,15 @@ public String getSimpleString() {
}
for (final ManaCostShard s : this.shards) {
if (s == ManaCostShard.X) {
sb.insert(0, s.toString());
sb.insert(0, s);
} else {
sb.append(s.toString());
}
}
// If the generic cost has been reduced below 0, display the reduction. (Only set for X cost spells)
if (this.genericCost < 0) {
sb.append(' ').append(this.genericCost);
}
return sb.toString();
}

Expand Down Expand Up @@ -295,6 +303,10 @@ public String getShortString() {
sb.append(' ');
sb.append(s);
}
// If the generic cost has been reduced below 0, display the reduction. (Only set for X cost spells)
if (generic < 0) {
sb.append(' ').append(generic);
}
return sb.toString().trim();
}

Expand Down Expand Up @@ -399,6 +411,12 @@ public int getGlyphCount() { // counts all colored shards or 1 for {0} costs
if (genericCost > 0 || (genericCost == 0 && width == 0)) {
width++;
}
// If the generic cost has been reduced below 0 (due to perpetual cost decrease effects)
// and there is an X cost (so the below 0 generic cost actually does something) then
// add space for an additional symbol to display the extra cost reduction.
if (genericCost < 0 && countX() > 0) {
width++;
}
return width;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,13 @@ public static void doAnimate(final Card c, final SpellAbility sa, final Integer

// give static abilities (should only be used by cards to give
// itself a static ability)
boolean costChange = false;
final List<StaticAbility> addedStaticAbilities = Lists.newArrayList();
for (final String s : stAbs) {
addedStaticAbilities.add(StaticAbility.create(AbilityUtils.getSVar(sa, s), c, sa.getCardState(), false));
if ("ReduceCost".equals(s) || "RaiseCost".equals(s)) {
costChange = true;
}
}

final GameCommand unanimate = new GameCommand() {
Expand Down Expand Up @@ -230,6 +234,9 @@ public String getDescription() {
addUntilCommand(sa, unanimate);
}
}
if (costChange) {
c.calculatePerpetualAdjustedManaCost();
}
}

/**
Expand Down
9 changes: 9 additions & 0 deletions forge-game/src/main/java/forge/game/card/Card.java
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ public void updateNonAbilityTextForView() {

public void updateManaCostForView() {
currentState.getView().updateManaCost(this);
currentState.calculatePerpetualAdjustedManaCost();

// TODO re-factor Spell ManaCost fallback to CardState ManaCost
if (getFirstSpellAbility() != null) {
Expand Down Expand Up @@ -2074,6 +2075,14 @@ public final ManaCost getManaCost() {
return result;
}

public void calculatePerpetualAdjustedManaCost() {
currentState.calculatePerpetualAdjustedManaCost();
}

public ManaCost getPerpetualAdjustedManaCost() {
return currentState.getPerpetualAdjustedManaCost();
}

public void addChangedManaCost(ManaCost cost, boolean additional, long timestamp, long staticId) {
changedCardManaCost.put(timestamp, staticId, new CardManaCost(cost, additional));
updateManaCostForView();
Expand Down
78 changes: 78 additions & 0 deletions forge-game/src/main/java/forge/game/card/CardState.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.*;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.CardTraitBase;
import forge.game.ForgeScript;
import forge.game.GameObject;
Expand All @@ -39,6 +41,7 @@
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellPermanent;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityMode;
import forge.game.trigger.Trigger;
import forge.util.CardTranslation;
import forge.util.ITranslatable;
Expand All @@ -52,6 +55,7 @@
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
Expand All @@ -62,6 +66,8 @@ public class CardState implements GameObject, IHasSVars, ITranslatable {
private CardType type = new CardType(false);
private CardTypeView changedType = null;
private ManaCost manaCost = ManaCost.NO_COST;
// Track mana cost after adjustments from perpetual cost-changing effects for display
private ManaCost perpetualAdjustedManaCost = null;
private ColorSet color = ColorSet.C;
private String oracleText = "";
private String functionalVariantName = null;
Expand Down Expand Up @@ -207,9 +213,81 @@ public final ManaCost getManaCost() {
}
public final void setManaCost(final ManaCost manaCost0) {
manaCost = manaCost0;
calculatePerpetualAdjustedManaCost();
}

/**
* Calculate and save the value of the mana cost adjusted by any perpetual raise/lower cost
* effects for display.
*/
public void calculatePerpetualAdjustedManaCost() {
final List<StaticAbility> raiseAbilities = Lists.newArrayList();
final List<StaticAbility> reduceAbilities = Lists.newArrayList();
// Separate abilities to apply them in proper order
if (getCard() == null || getCard().getGame() == null) {
setPerpetualAdjustedManaCost(manaCost);
return;
}
ManaCost manaCost = getCard().getManaCost();
// I don't know why refetching the card data like in this next line is necessary but in some cases (when the cost reduction
// wasn't added when the card was in the current zone, I think) the static abilities are missing. There's probably
// a better way to do this.
for (final StaticAbility stAb : getCard().getGame().getCardsInGame().get(getCard()).getStaticAbilities()) {
// for (final StaticAbility stAb : getStaticAbilities()) { // For some reason the current state sometimes doesn't have static abilities
// Only collect perpetual cost changes
if ("Card.Self".equals(stAb.getParam("ValidCard")) && "DBCleanup".equals(stAb.getParam("SubAbility"))) {
if (stAb.checkMode(StaticAbilityMode.ReduceCost)) {
reduceAbilities.add(stAb);
} else if (stAb.checkMode(StaticAbilityMode.RaiseCost)) {
raiseAbilities.add(stAb);
}
}
}

int totalGenericCostAdjustment = 0;
for (final StaticAbility stAb : raiseAbilities) {
int amount = Integer.parseInt(stAb.getParamOrDefault("Amount", "1"));
totalGenericCostAdjustment = totalGenericCostAdjustment + amount;
}
for (final StaticAbility stAb : reduceAbilities) {
int amount = Integer.parseInt(stAb.getParamOrDefault("Amount", "1"));
totalGenericCostAdjustment = totalGenericCostAdjustment - amount;
}

if (totalGenericCostAdjustment != 0) {
int genericCost = manaCost.getGenericCost();
int genericCostAdjustment;
int remainingGenericCostAdjustment;
if (genericCost + totalGenericCostAdjustment < 0) {
genericCostAdjustment = -genericCost;
remainingGenericCostAdjustment = totalGenericCostAdjustment + genericCost;
} else {
genericCostAdjustment = totalGenericCostAdjustment;
remainingGenericCostAdjustment = 0;
}
if (genericCostAdjustment != 0) {
String manaCostString = manaCost.getShortString();
manaCostString = manaCostString.replace("" + genericCost, "" + (genericCost + genericCostAdjustment));
manaCost = new ManaCost(new ManaCostParser(manaCostString));
}
if (remainingGenericCostAdjustment != 0 && manaCost.getShortString().contains("X")) {
// Store extra cost adjustment for X cost spells as a negative generic value for display
String manaCostString = manaCost.getShortString();
manaCost = new ManaCost(new ManaCostParser(manaCostString + " " + remainingGenericCostAdjustment));
}
}
setPerpetualAdjustedManaCost(manaCost);
view.updateManaCost(this);
}

public ManaCost getPerpetualAdjustedManaCost() {
return perpetualAdjustedManaCost;
}

private void setPerpetualAdjustedManaCost(ManaCost manaCost) {
perpetualAdjustedManaCost = manaCost;
}

public final ColorSet getColor() {
return color;
}
Expand Down
9 changes: 7 additions & 2 deletions forge-game/src/main/java/forge/game/card/CardView.java
Original file line number Diff line number Diff line change
Expand Up @@ -1388,11 +1388,16 @@ void updateType(CardState c) {
public ManaCost getManaCost() {
return get(TrackableProperty.ManaCost);
}
public ManaCost getOriginalManaCost() {
return get(TrackableProperty.OriginalManaCost);
}
void updateManaCost(CardState c) {
set(TrackableProperty.ManaCost, c.getManaCost());
set(TrackableProperty.ManaCost, c.getPerpetualAdjustedManaCost());
set(TrackableProperty.OriginalManaCost, c.getManaCost());
}
void updateManaCost(Card c) {
set(TrackableProperty.ManaCost, c.getManaCost());
set(TrackableProperty.ManaCost, c.getPerpetualAdjustedManaCost());
set(TrackableProperty.OriginalManaCost, c.getManaCost());
}

public String getOracleText() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public enum TrackableProperty {
ImageKey(TrackableTypes.StringType),
Type(TrackableTypes.CardTypeViewType),
ManaCost(TrackableTypes.ManaCostType),
OriginalManaCost(TrackableTypes.ManaCostType),
SetCode(TrackableTypes.StringType),
Rarity(TrackableTypes.EnumType(CardRarity.class)),
FunctionalVariant(TrackableTypes.StringType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,14 @@ public final void setCard(final CardView card, final boolean mayView, final bool
}

final String name = CardDetailUtil.formatCardName(card, mayView, isInAltState), nameCost;
if (state.getManaCost().isNoCost() || !mayView) {
if (state.getOriginalManaCost().isNoCost() || !mayView) {
nameCost = name;
} else {
final String manaCost;
if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack && card.getZone() != ZoneType.Battlefield) { //only display current state's mana cost when on stack
manaCost = card.getLeftSplitState().getManaCost() + " // " + card.getAlternateState().getManaCost();
manaCost = card.getLeftSplitState().getOriginalManaCost() + " // " + card.getAlternateState().getOriginalManaCost();
} else {
manaCost = state.getManaCost().toString();
manaCost = state.getOriginalManaCost().toString();
}
nameCost = String.format("%s - %s", name, manaCost);
}
Expand Down
17 changes: 15 additions & 2 deletions forge-gui-desktop/src/main/java/forge/toolbox/CardFaceSymbols.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package forge.toolbox;

import java.awt.Graphics;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
Expand Down Expand Up @@ -202,6 +202,19 @@ public static void draw(final Graphics g, final ManaCost manaCost, final int x,
xpos += offset;
}
}
// Show "negative" mana cost caused by perpetual cost reduction effects
// This is only relevant for cards with an "X" in the cost
if (genericManaCost < 0) {
final String sGenericAdjust = Integer.toString(Math.abs(genericManaCost));
drawSymbol(sGenericAdjust, g, xpos, y, size);
// Give it a yellow border so it doesn't look like the regular generic mana symbol
Stroke oldStroke = ((Graphics2D) g).getStroke();
((Graphics2D) g).setStroke(new BasicStroke(2));
g.setColor(Color.YELLOW);
g.drawOval(xpos, y, size, size);
((Graphics2D) g).setStroke(oldStroke);
xpos += offset;
}
}

public static void drawColorSet(Graphics g, ColorSet colorSet, int x, int y, int imageSize, boolean vertical) {
Expand Down Expand Up @@ -284,7 +297,7 @@ public static void drawAbilitySymbol(final String imageName, final Graphics g, f

/**
* <p>
* getWidth.
* Return width needed to draw mana symbols
* </p>
*
* @param manaCost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ private static void drawHeader(Graphics2D g, CardStateView state, Color[] colors

//draw mana cost for card
if (drawMana) {
ManaCost manaCost = state.getManaCost();
ManaCost manaCost = state.getOriginalManaCost();
int manaCostWidth = manaCost.getGlyphCount() * NAME_SIZE + HEADER_PADDING;
CardFaceSymbols.draw(g, manaCost, x + w - manaCostWidth, y + (h - NAME_SIZE) / 2 + 1, NAME_SIZE - 1);
w -= padding + manaCostWidth;
Expand Down
21 changes: 17 additions & 4 deletions forge-gui-mobile/src/forge/card/CardFaceSymbols.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
*/
package forge.card;

import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import com.badlogic.gdx.graphics.Color;
import forge.Forge;
import forge.Graphics;
import forge.assets.FSkinImage;
Expand All @@ -30,6 +27,10 @@
import forge.gui.error.BugReporter;
import forge.localinstance.skin.FSkinProp;

import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;


public class CardFaceSymbols {
public static final float FONT_SIZE_FACTOR = 0.85f;
Expand Down Expand Up @@ -178,6 +179,15 @@ public static void drawManaCost(Graphics g, ManaCost manaCost, float x, float y,
x += dx;
}
}
// Show "negative" mana cost caused by perpetual cost reduction effects
// This is only relevant for cards with an "X" in the cost
if (genericManaCost < 0) {
final String sGenericAdjust = Integer.toString(Math.abs(genericManaCost));
drawSymbol(sGenericAdjust, g, x, y, imageSize, imageSize);
// Give it a yellow border so it doesn't look like the regular generic mana symbol
g.drawCircle(3, Color.YELLOW, x + dx / 2, y + dx / 2, imageSize / 2 - 1);
x += dx;
}
}

public static void drawColorSet(Graphics g, ColorSet colorSet, float x, float y, final float imageSize) {
Expand Down Expand Up @@ -235,6 +245,9 @@ public static void drawSymbol(final String imageName, final Graphics g, final fl
g.drawImage(Forge.getAssets().manaImages().get(imageName), x, y, w, h);
}

/**
* Return width needed to draw mana symbols
*/
public static float getWidth(final ManaCost manaCost, float imageSize) {
return manaCost.getGlyphCount() * imageSize;
}
Expand Down
2 changes: 1 addition & 1 deletion forge-gui-mobile/src/forge/card/CardImageRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ private static void drawHeader(Graphics g, CardView card, CardStateView state, C
float manaSymbolSize = isAdventure ? MANA_SYMBOL_SIZE * 0.75f : MANA_SYMBOL_SIZE;
if (!noText && state != null) {
//draw mana cost for card
ManaCost mainManaCost = state.getManaCost();
ManaCost mainManaCost = state.getOriginalManaCost();
if (card.isSplitCard() && card.getAlternateState() != null && !card.isFaceDown() && card.getZone() != ZoneType.Stack && card.getZone() != ZoneType.Battlefield) {
//handle rendering both parts of split card
mainManaCost = card.getLeftSplitState().getManaCost();
Expand Down