diff --git a/forge-core/src/main/java/forge/card/CardFace.java b/forge-core/src/main/java/forge/card/CardFace.java index 4543aeb1be6..fa552d2907c 100644 --- a/forge-core/src/main/java/forge/card/CardFace.java +++ b/forge-core/src/main/java/forge/card/CardFace.java @@ -28,11 +28,12 @@ final class CardFace implements ICardFace, Cloneable { private final static List emptyList = Collections.unmodifiableList(new ArrayList<>()); private final static Map emptyMap = Collections.unmodifiableMap(new TreeMap<>()); + private final static Set emptySet = Collections.unmodifiableSet(new HashSet<>()); private String name; private String altName = null; private CardType type = null; - private ManaCost manaCost = ManaCost.NO_COST; + private ManaCost manaCost = null; private ColorSet color = null; private String oracleText = null; @@ -147,7 +148,7 @@ final class CardFace implements ICardFace, Cloneable { //Functional variant methods. Used for Attractions and some Un-cards, //when cards with the same name can have different logic. - public boolean hasFunctionalVariants() { + @Override public boolean hasFunctionalVariants() { return this.functionalVariants != null; } @Override public ICardFace getFunctionalVariant(String variant) { @@ -169,6 +170,7 @@ final class CardFace implements ICardFace, Cloneable { void assignMissingFields() { // Most scripts do not specify color explicitly if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; } if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color"); + if ( manaCost == null ) manaCost = ManaCost.NO_COST; if ( color == null ) color = ColorSet.fromManaCost(manaCost); if ( keywords == null ) keywords = emptyList; @@ -178,7 +180,54 @@ final class CardFace implements ICardFace, Cloneable { if ( replacements == null ) replacements = emptyList; if ( variables == null ) variables = emptyMap; if ( null == nonAbilityText ) nonAbilityText = ""; - //Not assigning attractionLightVariants here. Too rarely used. Will test for it downstream. + if ( attractionLights == null) attractionLights = emptySet; + + if(this.functionalVariants != null) { + //Copy fields to undefined ones in functional variants + for (CardFace variant : this.functionalVariants.values()) { + if(variant.oracleText == null) variant.oracleText = this.oracleText; + if(variant.manaCost == null) variant.manaCost = this.manaCost; + if(variant.color == null) variant.color = ColorSet.fromManaCost(variant.manaCost); + + if(variant.type == null) variant.type = this.type; + + if(variant.power == null) { + variant.power = this.power; + variant.iPower = this.iPower; + } + if(variant.toughness == null) { + variant.toughness = this.toughness; + variant.iToughness = this.iToughness; + } + + if("".equals(variant.initialLoyalty)) variant.initialLoyalty = this.initialLoyalty; + if("".equals(variant.defense)) variant.defense = this.defense; + + //variant.assignMissingFields(); + if(variant.keywords == null) variant.keywords = this.keywords; + else variant.keywords.addAll(0, this.keywords); + + if(variant.abilities == null) variant.abilities = this.abilities; + else variant.abilities.addAll(0, this.abilities); + + if(variant.staticAbilities == null) variant.staticAbilities = this.staticAbilities; + else variant.staticAbilities.addAll(0, this.staticAbilities); + + if(variant.triggers == null) variant.triggers = this.triggers; + else variant.triggers.addAll(0, this.triggers); + + if(variant.replacements == null) variant.replacements = this.replacements; + else variant.replacements.addAll(0, this.replacements); + + if(variant.variables == null) variant.variables = this.variables; + else variant.variables.putAll(this.variables); + + if(variant.nonAbilityText == null) variant.nonAbilityText = this.nonAbilityText; + if(variant.draftActions == null) variant.draftActions = this.draftActions; + if(variant.attractionLights == null) variant.attractionLights = this.attractionLights; + if(variant.altName == null) variant.altName = this.altName; + } + } } diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index e4f32f2fd22..e3789c5f444 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -5,6 +5,7 @@ import com.google.common.collect.Lists; import forge.card.CardRarity; import forge.card.CardRules; import forge.card.CardType.CoreType; +import forge.card.ICardFace; import forge.card.MagicColor; import forge.util.PredicateCard; import forge.util.PredicateString; @@ -253,6 +254,8 @@ public interface IPaperCard extends InventoryItem, Serializable { String getArtist(); String getItemType(); boolean hasBackFace(); + ICardFace getMainFace(); + ICardFace getOtherFace(); String getCardImageKey(); String getCardAltImageKey(); String getCardWSpecImageKey(); diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 0cf9021d309..8249607822a 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -178,7 +178,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) { this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false, - IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME, ""); + IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME, IPaperCard.NO_FUNCTIONAL_VARIANT); } public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, @@ -417,6 +417,29 @@ public class PaperCard implements Comparable, InventoryItemFromSet, || cst == CardSplitType.Modal; } + @Override + public ICardFace getMainFace() { + ICardFace face = this.rules.getMainPart(); + return this.getVariantForFace(face); + } + + @Override + public ICardFace getOtherFace() { + ICardFace face = this.rules.getOtherPart(); + return this.getVariantForFace(face); + } + + private ICardFace getVariantForFace(ICardFace face) { + if(!face.hasFunctionalVariants() || this.functionalVariant.equals(NO_FUNCTIONAL_VARIANT)) + return face; + ICardFace variant = face.getFunctionalVariant(this.functionalVariant); + if(variant == null) { + System.err.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", face.getName(), this.functionalVariant); + return face; + } + return variant; + } + // Return true if card is one of the five basic lands that can be added for free public boolean isVeryBasicLand() { return (this.getName().equals("Swamp")) diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index 4c3c5ef6d3d..91b900591f8 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -3,14 +3,10 @@ package forge.item; import java.util.ArrayList; import java.util.Locale; +import forge.card.*; import org.apache.commons.lang3.StringUtils; import forge.ImageKeys; -import forge.card.CardEdition; -import forge.card.CardRarity; -import forge.card.CardRules; -import forge.card.CardSplitType; -import forge.card.ColorSet; import forge.util.MyRandom; public class PaperToken implements InventoryItemFromSet, IPaperCard { @@ -204,6 +200,16 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard { return cst == CardSplitType.Transform; } + @Override + public ICardFace getMainFace() { + return this.getRules().getMainPart(); + } + + @Override + public ICardFace getOtherFace() { + return this.getRules().getOtherPart(); + } + @Override public boolean isToken() { return true; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index b9584b458b7..f5d8c518271 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -357,6 +357,18 @@ public class CardFactory { } private static void readCardFace(Card c, ICardFace face) { + //If it's a functional variant card, switch to that first. + if(face.hasFunctionalVariants()) { + String variantName = c.getPaperCard().getFunctionalVariant(); + if (!IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName)) { + ICardFace variant = face.getFunctionalVariant(variantName); + if (variant != null) + face = variant; + else + System.err.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", face.getName(), variantName); + } + } + // Build English oracle and translated oracle mapping if (c.getId() >= 0) { CardTranslation.buildOracleMapping(face.getName(), face.getOracleText()); @@ -417,73 +429,6 @@ public class CardFactory { } CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities()); - - if (face.hasFunctionalVariants()) { - applyFunctionalVariant(c, face); - } - } - - private static void applyFunctionalVariant(Card c, ICardFace originalFace) { - String variantName = c.getPaperCard().getFunctionalVariant(); - if (IPaperCard.NO_FUNCTIONAL_VARIANT.equals(variantName)) - return; - ICardFace variant = originalFace.getFunctionalVariant(variantName); - if (variant == null) { - System.out.printf("Tried to apply unknown or unsupported variant - Card: \"%s\"; Variant: %s\n", originalFace.getName(), variantName); - return; - } - - if (variant.getVariables() != null) - for (Entry v : variant.getVariables()) - c.setSVar(v.getKey(), v.getValue()); - if (variant.getReplacements() != null) - for (String r : variant.getReplacements()) - c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true, c.getCurrentState())); - if (variant.getStaticAbilities() != null) - for (String s : variant.getStaticAbilities()) - c.addStaticAbility(s); - if (variant.getTriggers() != null) - for (String t : variant.getTriggers()) - c.addTrigger(TriggerHandler.parseTrigger(t, c, true, c.getCurrentState())); - - if (variant.getKeywords() != null) - c.addIntrinsicKeywords(variant.getKeywords(), false); - - if (variant.getManaCost() != ManaCost.NO_COST) - c.setManaCost(variant.getManaCost()); - if (variant.getNonAbilityText() != null) - c.setText(variant.getNonAbilityText()); - - if (!"".equals(variant.getInitialLoyalty())) - c.getCurrentState().setBaseLoyalty(variant.getInitialLoyalty()); - if (!"".equals(variant.getDefense())) - c.getCurrentState().setBaseDefense(variant.getDefense()); - - if (variant.getOracleText() != null) - c.getCurrentState().setOracleText(variant.getOracleText()); - - if (variant.getType() != null) { - for(String type : variant.getType()) - c.addType(type); - } - - if (variant.getColor() != null) - c.setColor(variant.getColor().getColor()); - - if (variant.getIntPower() != Integer.MAX_VALUE) { - c.setBasePower(variant.getIntPower()); - c.setBasePowerString(variant.getPower()); - } - if (variant.getIntToughness() != Integer.MAX_VALUE) { - c.setBaseToughness(variant.getIntToughness()); - c.setBaseToughnessString(variant.getToughness()); - } - - if (variant.getAttractionLights() != null) - c.setAttractionLights(variant.getAttractionLights()); - - if (variant.getAbilities() != null) - CardFactoryUtil.addAbilityFactoryAbilities(c, variant.getAbilities()); } public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki, final boolean keepTextChanges) { diff --git a/forge-gui-desktop/src/main/java/forge/gui/CardDetailPanel.java b/forge-gui-desktop/src/main/java/forge/gui/CardDetailPanel.java index 93bc83b0b6b..e2afdd6454c 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/CardDetailPanel.java +++ b/forge-gui-desktop/src/main/java/forge/gui/CardDetailPanel.java @@ -251,7 +251,7 @@ public class CardDetailPanel extends SkinnedPanel { updateBorder(state, mayView); } - powerToughnessLabel.setText(CardDetailUtil.formatPrimaryCharacteristic(state, mayView)); + powerToughnessLabel.setText(FSkin.encodeSymbols(CardDetailUtil.formatPrimaryCharacteristic(state, mayView), false)); idLabel.setText(mayView ? CardDetailUtil.formatCardId(state) : ""); diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/views/AttractionLightRenderer.java b/forge-gui-desktop/src/main/java/forge/itemmanager/views/AttractionLightRenderer.java new file mode 100644 index 00000000000..a9d2e052937 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/views/AttractionLightRenderer.java @@ -0,0 +1,69 @@ +package forge.itemmanager.views; + +import forge.toolbox.CardFaceSymbols; +import org.apache.commons.lang3.StringUtils; + +import javax.swing.*; +import java.awt.*; +import java.util.Set; + +public class AttractionLightRenderer extends ItemCellRenderer { + private static final int elementWidth = 13; + private static final int elementGap = 2; + private static final int padding = 2; + + //Can't check for type params via instanceof, but it doesn't really matter since all we're using is .contains() + private Set lights; + + /* + * (non-Javadoc) + * + * @see + * javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent + * (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int) + */ + @Override + public final Component getTableCellRendererComponent(final JTable table, final Object value, + final boolean isSelected, final boolean hasFocus, final int row, final int column) { + + if (value instanceof Set) { + this.lights = (Set) value; + this.setToolTipText(StringUtils.join(this.lights, ", ")); + } + else { + this.lights = null; + this.setToolTipText(null); + } + + return super.getTableCellRendererComponent(table, "", isSelected, hasFocus, row, column); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.JComponent#paint(java.awt.Graphics) + */ + @Override + public final void paint(final Graphics g) { + super.paint(g); + + if(this.lights == null) + return; + + final int cellWidth = this.getWidth(); + + int x = padding; + int y = padding + 1; + + final int cntGlyphs = 6; + final int offsetIfNoSpace = (cellWidth - padding - elementWidth) / (cntGlyphs - 1); + final int dx = Math.min(elementWidth + elementGap, offsetIfNoSpace); + + CardFaceSymbols.drawManaSymbol(lights.contains(1) ? "AL1ON" : "AL1OFF", g, x, y); + CardFaceSymbols.drawManaSymbol(lights.contains(2) ? "AL2ON" : "AL2OFF", g, x + (dx), y); + CardFaceSymbols.drawManaSymbol(lights.contains(3) ? "AL3ON" : "AL3OFF", g, x + (dx * 2), y); + CardFaceSymbols.drawManaSymbol(lights.contains(4) ? "AL4ON" : "AL4OFF", g, x + (dx * 3), y); + CardFaceSymbols.drawManaSymbol(lights.contains(5) ? "AL5ON" : "AL5OFF", g, x + (dx * 4), y); + CardFaceSymbols.drawManaSymbol(lights.contains(6) ? "AL6ON" : "AL6OFF", g, x + (dx * 5), y); + } +} diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ItemCellRenderer.java b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ItemCellRenderer.java index 93ea8eaf42c..4863a73e37b 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ItemCellRenderer.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ItemCellRenderer.java @@ -50,6 +50,8 @@ public class ItemCellRenderer extends DefaultTableCellRenderer { return new SetCodeRenderer(); case COST: return new ManaCostRenderer(); + case ATTRACTION_LIGHTS: + return new AttractionLightRenderer(); case DECK_COLOR: return new ColorSetRenderer(); case FAVORITE: diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java index 879a42d0eea..ec10f0b288f 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java @@ -1120,7 +1120,7 @@ public class FSkin { private static String preferredName; private static BufferedImage bimDefaultSprite, bimFavIcon, bimPreferredSprite, bimFoils, bimQuestDraftDeck, bimOldFoils, bimDefaultAvatars, bimPreferredAvatars, bimTrophies, bimAbilities, bimManaIcons, bimPhyrexian, bimColorlessHybrid, bimDefaultSleeve, - bimDefaultSleeve2, bimDefaultDeckbox, bimPrefferedSetLogo, bimDefaultWatermark, bimDefaultDraftRank; + bimDefaultSleeve2, bimDefaultDeckbox, bimPrefferedSetLogo, bimDefaultWatermark, bimDefaultDraftRank, bimAttractionLights; private static int x0, y0, w0, h0, newW, newH, preferredW, preferredH; private static int defaultFontSize = 12; private static boolean loaded = false; @@ -1239,7 +1239,7 @@ public class FSkin { } final Localizer localizer = Localizer.getInstance(); - FView.SINGLETON_INSTANCE.setSplashProgessBarMessage(localizer.getMessage("splash.loading.processingimagesprites") + ": ", 12); + FView.SINGLETON_INSTANCE.setSplashProgessBarMessage(localizer.getMessage("splash.loading.processingimagesprites") + ": ", 20); // Grab and test various sprite files. final String defaultDir = ForgeConstants.DEFAULT_SKINS_DIR; @@ -1263,6 +1263,7 @@ public class FSkin { final File f18 = new File(defaultDir + ForgeConstants.SPRITE_PHYREXIAN_FILE); final File f19 = new File(defaultDir + ForgeConstants.SPRITE_COLORLESS_HYBRID_FILE); final File f20 = new File(defaultDir + ForgeConstants.SPRITE_DRAFTRANKS_FILE); + final File f21 = new File(defaultDir + ForgeConstants.SPRITE_ATTRACTION_LIGHTS_FILE); try { int p = 0; @@ -1276,6 +1277,8 @@ public class FSkin { FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p); bimColorlessHybrid = ImageIO.read(f19); FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p); + bimAttractionLights = ImageIO.read(f21); + FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p); bimPreferredSprite = ImageIO.read(f2); FView.SINGLETON_INSTANCE.incrementSplashProgessBar(++p); bimFoils = ImageIO.read(f3); @@ -1363,6 +1366,9 @@ public class FSkin { case COLORLESS_HYBRID: setImage(prop, bimColorlessHybrid); break; + case ATTRACTION_LIGHTS: + setImage(prop, bimAttractionLights); + break; case DECKBOX: setImage(prop, bimDefaultDeckbox); break; @@ -1407,6 +1413,7 @@ public class FSkin { bimPhyrexian.flush(); bimColorlessHybrid.flush(); bimManaIcons.flush(); + bimAttractionLights.flush(); if (bimPreferredAvatars != null) { bimPreferredAvatars.flush(); } @@ -1428,6 +1435,7 @@ public class FSkin { bimPhyrexian = null; bimColorlessHybrid = null; bimManaIcons = null; + bimAttractionLights = null; //establish encoding symbols final File dir = new File(ForgeConstants.CACHE_SYMBOLS_DIR); diff --git a/forge-gui-mobile/src/forge/card/CardFaceSymbols.java b/forge-gui-mobile/src/forge/card/CardFaceSymbols.java index b3544da21cb..1b4d1864af2 100644 --- a/forge-gui-mobile/src/forge/card/CardFaceSymbols.java +++ b/forge-gui-mobile/src/forge/card/CardFaceSymbols.java @@ -18,6 +18,7 @@ package forge.card; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import forge.Forge; @@ -194,6 +195,16 @@ public class CardFaceSymbols { } } + public static void drawAttractionLights(Graphics g, Set lights, float x, float y, final float imageSize, boolean vertical) { + for(int i = 1; i <= 6; i++) { + drawSymbol("AL" + i + (lights.contains(i) ? "ON" : "OFF"), g, x, y, imageSize, imageSize); + if (!vertical) + x += imageSize; + else + y += imageSize; + } + } + public static void drawOther(final Graphics g, String s, float x, final float y, final float w, final float h, boolean rotate) { if (s.length() == 0) { return; diff --git a/forge-gui-mobile/src/forge/card/CardImageRenderer.java b/forge-gui-mobile/src/forge/card/CardImageRenderer.java index 8cea56397aa..91a0d116e70 100644 --- a/forge-gui-mobile/src/forge/card/CardImageRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardImageRenderer.java @@ -11,6 +11,7 @@ import forge.ImageKeys; import forge.assets.*; import forge.item.PaperCard; import forge.util.ImageUtil; +import forge.util.TextBounds; import org.apache.commons.lang3.StringUtils; import com.badlogic.gdx.graphics.Color; @@ -1173,14 +1174,15 @@ public class CardImageRenderer { return; } + TextBounds bounds = cardTextRenderer.getBounds(ptText, PT_FONT); float padding = PT_FONT.getCapHeight() / 2; - float boxWidth = Math.min(PT_FONT.getBounds(ptText).width + 2 * padding, + float boxWidth = Math.min(bounds.width + 2 * padding, w - idWidth - padding); //prevent box overlapping ID x += w - boxWidth; w = boxWidth; fillColorBackground(g, colors, x, y, w, h); g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h); - g.drawText(ptText, PT_FONT, Color.BLACK, x, y, w, h, false, Align.center, true); + cardTextRenderer.drawText(g, ptText, PT_FONT, Color.BLACK, x, y, w, h, y, h, false, Align.center, true); } } diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java index 1e23c2d08bd..8be14341a78 100644 --- a/forge-gui-mobile/src/forge/card/CardRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardRenderer.java @@ -497,18 +497,25 @@ public class CardRenderer { //render card name and mana cost on first line float manaCostWidth = 0; ManaCost mainManaCost = cardCurrentState.getManaCost(); - if (card.isSplitCard()) { - //handle rendering both parts of split card - mainManaCost = card.getLeftSplitState().getManaCost(); - ManaCost otherManaCost = card.getAlternateState().getManaCost(); - manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + MANA_COST_PADDING; - CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth + MANA_COST_PADDING, y, MANA_SYMBOL_SIZE); - //draw "//" between two parts of mana cost - manaCostWidth += font.getBounds("//").width + MANA_COST_PADDING; - g.drawText("//", font, foreColor, x + w - manaCostWidth + MANA_COST_PADDING, y, w, MANA_SYMBOL_SIZE, false, Align.left, true); + if (!mainManaCost.isNoCost() || (card.isSplitCard() && !card.getLeftSplitState().getManaCost().isNoCost())) { + if (card.isSplitCard()) { + //handle rendering both parts of split card + mainManaCost = card.getLeftSplitState().getManaCost(); + ManaCost otherManaCost = card.getAlternateState().getManaCost(); + manaCostWidth = CardFaceSymbols.getWidth(otherManaCost, MANA_SYMBOL_SIZE) + MANA_COST_PADDING; + CardFaceSymbols.drawManaCost(g, otherManaCost, x + w - manaCostWidth + MANA_COST_PADDING, y, MANA_SYMBOL_SIZE); + //draw "//" between two parts of mana cost + manaCostWidth += font.getBounds("//").width + MANA_COST_PADDING; + g.drawText("//", font, foreColor, x + w - manaCostWidth + MANA_COST_PADDING, y, w, MANA_SYMBOL_SIZE, false, Align.left, true); + } + manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, MANA_SYMBOL_SIZE); + CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y, MANA_SYMBOL_SIZE); + } + else if(cardCurrentState.isAttraction()) { + //For attractions, draw their lights instead of a mana cost. + float lightWidth = (6 * MANA_SYMBOL_SIZE) + MANA_COST_PADDING; + CardFaceSymbols.drawAttractionLights(g, cardCurrentState.getAttractionLights(), x + w - lightWidth, y, MANA_SYMBOL_SIZE, false); } - manaCostWidth += CardFaceSymbols.getWidth(mainManaCost, MANA_SYMBOL_SIZE); - CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y, MANA_SYMBOL_SIZE); x += cardArtWidth; String name = CardTranslation.getTranslatedName(card.getCurrentState().getName()); @@ -544,9 +551,6 @@ public class CardRenderer { type += String.format(" [%s / %s]", power, toughness); } else if (cardCurrentState.isBattle()) { type += " (" + cardCurrentState.getDefense() + ")"; - } else if (cardCurrentState.isAttraction()) { - //TODO: Probably shouldn't be non-localized text here? Not sure what to do if someone makes an attraction with no lights... - type += " (" + (cardCurrentState.getAttractionLights().isEmpty() ? "No Lights" : StringUtils.join(cardCurrentState.getAttractionLights(), ", ")) + ")"; } g.drawText(type, typeFont, foreColor, x, y, availableTypeWidth, lineHeight, false, Align.left, true); } diff --git a/forge-gui/res/cardsfolder/b/backup_plan.txt b/forge-gui/res/cardsfolder/b/backup_plan.txt index 7e05458433c..92a47275fa4 100644 --- a/forge-gui/res/cardsfolder/b/backup_plan.txt +++ b/forge-gui/res/cardsfolder/b/backup_plan.txt @@ -1,4 +1,5 @@ Name:Backup Plan +ManaCost:no cost Types:Conspiracy Text:Draw an additional hand of seven cards as the game begins. Before taking mulligans, shuffle all but one of your hands into your library. -Oracle: (Start the game with this conspiracy face up in the command zone.)\nDraw an additional hand of seven cards as the game begins. Before taking mulligans, shuffle all but one of your hands into your library. +Oracle:(Start the game with this conspiracy face up in the command zone.)\nDraw an additional hand of seven cards as the game begins. Before taking mulligans, shuffle all but one of your hands into your library. diff --git a/forge-gui/res/cardsfolder/p/paliano_the_high_city.txt b/forge-gui/res/cardsfolder/p/paliano_the_high_city.txt index feda7616009..a46bdafc72a 100644 --- a/forge-gui/res/cardsfolder/p/paliano_the_high_city.txt +++ b/forge-gui/res/cardsfolder/p/paliano_the_high_city.txt @@ -1,4 +1,5 @@ Name:Paliano, the High City +ManaCost:no cost Types:Legendary Land Draft:Reveal CARDNAME as you draft it. Draft:As you draft CARDNAME, the player to your right chooses a color, you choose another color, then the player to your left chooses a third color. diff --git a/forge-gui/res/skins/default/sprite_attraction_lights.png b/forge-gui/res/skins/default/sprite_attraction_lights.png new file mode 100644 index 00000000000..65d2aec8d97 Binary files /dev/null and b/forge-gui/res/skins/default/sprite_attraction_lights.png differ diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index e20ff03bd72..68bb4f7a236 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -217,19 +217,22 @@ public class CardDetailUtil { } if (card.isAttraction()) { - ptText.append(Localizer.getInstance().getMessage("lblLights")).append(": ("); - Set lights = card.getAttractionLights(); - //TODO: It'd be really nice if the actual lights were drawn as symbols here. Need to look into that... - if (lights == null || lights.isEmpty()) - ptText.append(Localizer.getInstance().getMessage("lblNone")); - else - ptText.append(StringUtils.join(lights, ", ")); - ptText.append(")"); + ptText.append(Localizer.getInstance().getMessage("lblLights")).append(": "); + ptText.append(formatAttractionLights(card.getAttractionLights())); } return ptText.toString(); } + public static String formatAttractionLights(Set lights) { + return (lights.contains(1) ? "{AL1ON} " : "{AL1OFF} ") + + (lights.contains(2) ? "{AL2ON} " : "{AL2OFF} ") + + (lights.contains(3) ? "{AL3ON} " : "{AL3OFF} ") + + (lights.contains(4) ? "{AL4ON} " : "{AL4OFF} ") + + (lights.contains(5) ? "{AL5ON} " : "{AL5OFF} ") + + (lights.contains(6) ? "{AL6ON}" : "{AL6OFF}"); + } + public static String formatCardId(final CardStateView card) { final String id = card.getDisplayId(); return id.isEmpty() ? id : "[" + id + "]"; diff --git a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java index 53b0f8ecb0a..e36dce45757 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java @@ -198,6 +198,10 @@ public enum ColumnDef { return toCMC(from.getKey()); } }), + ATTRACTION_LIGHTS("lblLights", "lblLights", 94, true, SortState.NONE, + from -> toAttractionLightSort(from.getKey()), + from -> toAttractionLights(from.getKey()) + ), /** * The rarity column. */ @@ -615,10 +619,11 @@ public enum ColumnDef { private static Integer toPower(final InventoryItem i) { int result = Integer.MAX_VALUE; if (i instanceof PaperCard) { - result = ((IPaperCard) i).getRules().getIntPower(); + ICardFace face = ((IPaperCard) i).getMainFace(); + result = face.getIntPower(); if (result == Integer.MAX_VALUE) { - if (((IPaperCard) i).getRules().getType().isPlaneswalker()) { - String loy = ((IPaperCard) i).getRules().getInitialLoyalty(); + if (face.getType().isPlaneswalker()) { + String loy = face.getInitialLoyalty(); result = StringUtils.isNumeric(loy) ? Integer.parseInt(loy) : 0; } } @@ -627,7 +632,7 @@ public enum ColumnDef { } private static Integer toToughness(final InventoryItem i) { - return i instanceof PaperCard ? ((IPaperCard) i).getRules().getIntToughness() : Integer.MAX_VALUE; + return i instanceof PaperCard ? ((IPaperCard) i).getMainFace().getIntToughness() : Integer.MAX_VALUE; } private static Integer toCMC(final InventoryItem i) { @@ -703,7 +708,7 @@ public enum ColumnDef { * @return Part of a sortable numeric string. */ private static String toArtifactsWithColorlessCostsLast(final InventoryItem i) { - forge.card.mana.ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost(); + ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost(); return !(((IPaperCard) i).getRules().getType().isArtifact() && (toColor(i).isColorless() || //If it isn't colorless, see if it can be paid with only white, only blue, only black. @@ -766,7 +771,7 @@ public enum ColumnDef { * @return Part of a sortable numeric string. */ private static String toGoldFirst(final InventoryItem i) { - forge.card.mana.ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost(); + ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost(); return !(manaCost.canBePaidWithAvailable(MagicColor.WHITE) | manaCost.canBePaidWithAvailable(MagicColor.BLUE) | manaCost.canBePaidWithAvailable(MagicColor.BLACK) | manaCost.canBePaidWithAvailable(MagicColor.RED) | @@ -784,9 +789,8 @@ public enum ColumnDef { //Split card sorting is probably as complex as sorting gets. //This method serves as an entry point only, separating the two card parts for convenience. private static String toSplitCardSort(final InventoryItem i) { - CardRules rules = ((IPaperCard) i).getRules(); - forge.card.ICardFace mainPart = rules.getMainPart(); - forge.card.ICardFace otherPart = rules.getOtherPart(); + ICardFace mainPart = ((IPaperCard) i).getMainFace(); + ICardFace otherPart = ((IPaperCard) i).getOtherFace(); return toSplitSort(mainPart, otherPart); } @@ -924,4 +928,20 @@ public enum ColumnDef { ) ); } + + private static Set toAttractionLights(final InventoryItem i) { + return i instanceof PaperCard ? ((PaperCard) i).getMainFace().getAttractionLights() : null; + } + + private static String toAttractionLightSort(final InventoryItem i) { + if(!(i instanceof PaperCard)) + return ""; + Set lights = ((PaperCard) i).getRules().getAttractionLights(); + return (lights.contains(1) ? "0" : "1") + + (lights.contains(2) ? "0" : "1") + + (lights.contains(3) ? "0" : "1") + + (lights.contains(4) ? "0" : "1") + + (lights.contains(5) ? "0" : "1") + + (lights.contains(6) ? "0" : "1"); + } } diff --git a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java index 7a8bf4efbc4..ff0328f4e28 100644 --- a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java +++ b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java @@ -148,6 +148,7 @@ public final class SColumnUtil { List colDefs = new ArrayList<>(); colDefs.add(ColumnDef.FAVORITE); colDefs.add(ColumnDef.NAME); + colDefs.add(ColumnDef.ATTRACTION_LIGHTS); colDefs.add(ColumnDef.RARITY); colDefs.add(ColumnDef.SET); colDefs.add(ColumnDef.COLLECTOR_ORDER); diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java index c97f562a861..9016e25f5f4 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java @@ -113,30 +113,32 @@ public final class ForgeConstants { public static final String COMMON_FONTS_DIR = RES_DIR + "fonts" + PATH_SEPARATOR; public static final String DEFAULT_SKINS_DIR = BASE_SKINS_DIR + "default" + PATH_SEPARATOR; //don't associate these skin files with a directory since skin directory will be determined later - public static final String SPRITE_ICONS_FILE = "sprite_icons.png"; - public static final String SPRITE_FOILS_FILE = "sprite_foils.png"; - public static final String SPRITE_OLD_FOILS_FILE = "sprite_old_foils.png"; - public static final String SPRITE_TROPHIES_FILE = "sprite_trophies.png"; - public static final String SPRITE_ABILITY_FILE = "sprite_ability.png"; - public static final String SPRITE_BORDER_FILE = "sprite_border.png"; - public static final String SPRITE_ADV_BUTTONS_FILE = "sprite_adv_buttons.png"; - public static final String SPRITE_BUTTONS_FILE = "sprite_buttons.png"; - public static final String SPRITE_DECKBOX_FILE = "sprite_deckbox.png"; - public static final String SPRITE_START_FILE = "sprite_start.png"; - public static final String SPRITE_MANAICONS_FILE = "sprite_manaicons.png"; - public static final String SPRITE_PHYREXIAN_FILE = "sprite_phyrexian.png"; - public static final String SPRITE_COLORLESS_HYBRID_FILE = "sprite_hybrid_colorless.png"; - public static final String SPRITE_CURSOR_FILE = "sprite_cursor.png"; - public static final String SPRITE_AVATARS_FILE = "sprite_avatars.png"; - public static final String SPRITE_CRACKS_FILE = "sprite_cracks.png"; - public static final String SPRITE_SLEEVES_FILE = "sprite_sleeves.png"; - public static final String SPRITE_SLEEVES2_FILE = "sprite_sleeves2.png"; - public static final String SPRITE_FAVICONS_FILE = "sprite_favicons.png"; - public static final String SPRITE_PLANAR_CONQUEST_FILE = "sprite_planar_conquest.png"; - public static final String SPRITE_ADVENTURE_FILE = "sprite_adventure.png"; - public static final String SPRITE_SETLOGO_FILE = "sprite_setlogo.png"; - public static final String SPRITE_WATERMARK_FILE = "sprite_watermark.png"; - public static final String SPRITE_DRAFTRANKS_FILE = "sprite_draftranks.png"; + public static final String SPRITE_ICONS_FILE = "sprite_icons.png"; + public static final String SPRITE_FOILS_FILE = "sprite_foils.png"; + public static final String SPRITE_OLD_FOILS_FILE = "sprite_old_foils.png"; + public static final String SPRITE_TROPHIES_FILE = "sprite_trophies.png"; + public static final String SPRITE_ABILITY_FILE = "sprite_ability.png"; + public static final String SPRITE_BORDER_FILE = "sprite_border.png"; + public static final String SPRITE_ADV_BUTTONS_FILE = "sprite_adv_buttons.png"; + public static final String SPRITE_BUTTONS_FILE = "sprite_buttons.png"; + public static final String SPRITE_DECKBOX_FILE = "sprite_deckbox.png"; + public static final String SPRITE_START_FILE = "sprite_start.png"; + public static final String SPRITE_MANAICONS_FILE = "sprite_manaicons.png"; + public static final String SPRITE_PHYREXIAN_FILE = "sprite_phyrexian.png"; + public static final String SPRITE_COLORLESS_HYBRID_FILE = "sprite_hybrid_colorless.png"; + public static final String SPRITE_ATTRACTION_LIGHTS_FILE = "sprite_attraction_lights.png"; + public static final String SPRITE_CURSOR_FILE = "sprite_cursor.png"; + public static final String SPRITE_AVATARS_FILE = "sprite_avatars.png"; + public static final String SPRITE_CRACKS_FILE = "sprite_cracks.png"; + public static final String SPRITE_SLEEVES_FILE = "sprite_sleeves.png"; + public static final String SPRITE_SLEEVES2_FILE = "sprite_sleeves2.png"; + public static final String SPRITE_FAVICONS_FILE = "sprite_favicons.png"; + public static final String SPRITE_PLANAR_CONQUEST_FILE = "sprite_planar_conquest.png"; + public static final String SPRITE_ADVENTURE_FILE = "sprite_adventure.png"; + public static final String SPRITE_SETLOGO_FILE = "sprite_setlogo.png"; + public static final String SPRITE_WATERMARK_FILE = "sprite_watermark.png"; + public static final String SPRITE_DRAFTRANKS_FILE = "sprite_draftranks.png"; + public static final String FONT_FILE = "font1.ttf"; public static final String SPLASH_BG_FILE = "bg_splash.png"; public static final String MATCH_BG_FILE = "bg_match.jpg"; diff --git a/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java b/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java index 1d6ad60e62e..f07f1d21d44 100644 --- a/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java +++ b/forge-gui/src/main/java/forge/localinstance/skin/FSkinProp.java @@ -177,6 +177,20 @@ public enum FSkinProp { IMG_CMC_MID_HIGH (new int[] {2, 166, 160, 160}, PropType.MANAICONS), IMG_CMC_HIGH (new int[] {2, 248, 160, 160}, PropType.MANAICONS), + //attraction lights + IMG_ATTR_1_ON (new int[] {0, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_2_ON (new int[] {210, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_3_ON (new int[] {420, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_4_ON (new int[] {630, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_5_ON (new int[] {840, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_6_ON (new int[] {1050, 0, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_1_OFF (new int[] {0, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_2_OFF (new int[] {210, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_3_OFF (new int[] {420, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_4_OFF (new int[] {630, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_5_OFF (new int[] {840, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + IMG_ATTR_6_OFF (new int[] {1050, 210, 200, 200}, PropType.ATTRACTION_LIGHTS), + //PAWPRINT IMG_PAWPRINT (new int[] {2, 902, 80, 80}, PropType.MANAICONS), @@ -660,6 +674,20 @@ public enum FSkinProp { MANA_IMG.put("Q", FSkinProp.IMG_UNTAP); MANA_IMG.put("T", FSkinProp.IMG_TAP); MANA_IMG.put("P", FSkinProp.IMG_PAWPRINT); + + //Attraction lights. Not really mana icons but they're loaded into the card symbols in all the same places. + MANA_IMG.put("AL1ON", FSkinProp.IMG_ATTR_1_ON); + MANA_IMG.put("AL2ON", FSkinProp.IMG_ATTR_2_ON); + MANA_IMG.put("AL3ON", FSkinProp.IMG_ATTR_3_ON); + MANA_IMG.put("AL4ON", FSkinProp.IMG_ATTR_4_ON); + MANA_IMG.put("AL5ON", FSkinProp.IMG_ATTR_5_ON); + MANA_IMG.put("AL6ON", FSkinProp.IMG_ATTR_6_ON); + MANA_IMG.put("AL1OFF", FSkinProp.IMG_ATTR_1_OFF); + MANA_IMG.put("AL2OFF", FSkinProp.IMG_ATTR_2_OFF); + MANA_IMG.put("AL3OFF", FSkinProp.IMG_ATTR_3_OFF); + MANA_IMG.put("AL4OFF", FSkinProp.IMG_ATTR_4_OFF); + MANA_IMG.put("AL5OFF", FSkinProp.IMG_ATTR_5_OFF); + MANA_IMG.put("AL6OFF", FSkinProp.IMG_ATTR_6_OFF); } public enum PropType { @@ -678,6 +706,7 @@ public enum FSkinProp { MANAICONS(ForgeConstants.SPRITE_MANAICONS_FILE), PHYREXIAN(ForgeConstants.SPRITE_PHYREXIAN_FILE), COLORLESS_HYBRID(ForgeConstants.SPRITE_COLORLESS_HYBRID_FILE), + ATTRACTION_LIGHTS(ForgeConstants.SPRITE_ATTRACTION_LIGHTS_FILE), PLANAR_CONQUEST(ForgeConstants.SPRITE_PLANAR_CONQUEST_FILE), ADVENTURE(ForgeConstants.SPRITE_ADVENTURE_FILE), DECKBOX(ForgeConstants.SPRITE_DECKBOX_FILE),