diff --git a/.gitattributes b/.gitattributes index ab5f60fdf6a..90d4bf6fa0e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1174,7 +1174,6 @@ forge-gui-desktop/src/main/java/forge/view/arcane/util/CardPanelMouseListener.ja forge-gui-desktop/src/main/java/forge/view/arcane/util/OutlinedLabel.java -text forge-gui-desktop/src/main/java/forge/view/arcane/util/package-info.java -text forge-gui-desktop/src/main/java/forge/view/package-info.java -text -forge-gui-desktop/src/main/resources/Roboto-Bold.ttf -text forge-gui-desktop/src/test/java/forge/BoosterDraft1Test.java -text forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java -text forge-gui-desktop/src/test/java/forge/CardRankerTest.java -text @@ -19084,6 +19083,7 @@ forge-gui/res/editions/Worldwake.txt -text forge-gui/res/editions/Zendikar[!!-~]Expeditions.txt -text forge-gui/res/editions/Zendikar.txt -text forge-gui/res/effects/lightning.gif -text +forge-gui/res/fonts/Roboto-Bold.ttf -text forge-gui/res/howto.txt svneol=native#text/plain forge-gui/res/languages/en-US.properties -text forge-gui/res/licenses/java-yield-license.txt svneol=native#text/plain diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 234f32728dc..4ef999e53c8 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -195,6 +195,7 @@ public enum CSubmenuPreferences implements ICDoc { initializeColorIdentityCombobox(); initializeAutoYieldModeComboBox(); initializeCounterDisplayTypeComboBox(); + initializeCounterDisplayLocationComboBox(); initializePlayerNameButton(); } @@ -360,6 +361,25 @@ public enum CSubmenuPreferences implements ICDoc { } + private void initializeCounterDisplayLocationComboBox() { + + final String[] elements = new String[ForgeConstants.CounterDisplayLocation.values().length]; + + ForgeConstants.CounterDisplayLocation[] values = ForgeConstants.CounterDisplayLocation.values(); + for (int i = 0; i < values.length; i++) { + elements[i] = values[i].getName(); + } + + final FPref userSetting = FPref.UI_CARD_COUNTER_DISPLAY_LOCATION; + final FComboBoxPanel panel = this.view.getCounterDisplayLocationComboBoxPanel(); + + final FComboBox comboBox = createComboBox(elements, userSetting); + final String selectedItem = this.prefs.getPref(userSetting); + + panel.setComboBox(comboBox, selectedItem); + + } + private FComboBox createComboBox(final E[] items, final ForgePreferences.FPref setting) { final FComboBox comboBox = new FComboBox<>(items); addComboBoxListener(comboBox, setting); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 7a36fd03d53..a79d83ae5bf 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -103,6 +103,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final FComboBoxPanel cbpDisplayCurrentCardColors = new FComboBoxPanel<>("Show Detailed Card Color:"); private final FComboBoxPanel cbpAutoYieldMode = new FComboBoxPanel<>("Auto-Yield:"); private final FComboBoxPanel cbpCounterDisplayType = new FComboBoxPanel<>("Counter Display Type:"); + private final FComboBoxPanel cbpCounterDisplayLocation = new FComboBoxPanel<>("Counter Display Location:"); /** * Constructor. @@ -280,6 +281,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbpCounterDisplayType, comboBoxConstraints); pnlPrefs.add(new NoteLabel("Selects the style of the in-game counter display for cards. Text-based is a new tab-like display on the cards. Image-based is the old counter image. Hybrid displays both at once."), descriptionConstraints); + pnlPrefs.add(cbpCounterDisplayLocation, comboBoxConstraints); + pnlPrefs.add(new NoteLabel("Determines where to position the text-based counters on the card: close to the top or close to the bottom."), descriptionConstraints); + pnlPrefs.add(cbpDisplayCurrentCardColors, comboBoxConstraints); pnlPrefs.add(new NoteLabel("Displays the breakdown of the current color of cards in the card detail information panel."), descriptionConstraints); @@ -593,6 +597,10 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbpCounterDisplayType; } + public FComboBoxPanel getCounterDisplayLocationComboBoxPanel() { + return cbpCounterDisplayLocation; + } + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbEnforceDeckLegality() { return cbEnforceDeckLegality; diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java b/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java index edc0c132bb7..d9e7a90929f 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java @@ -29,6 +29,7 @@ import forge.game.card.CounterType; import forge.gui.CardContainer; import forge.item.PaperCard; import forge.model.FModel; +import forge.properties.ForgeConstants; import forge.properties.ForgeConstants.CounterDisplayType; import forge.properties.ForgePreferences.FPref; import forge.screens.match.CMatchUI; @@ -45,6 +46,7 @@ import java.awt.font.TextAttribute; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -99,7 +101,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl try { - Font roboto = Font.createFont(Font.TRUETYPE_FONT, CardPanel.class.getClassLoader().getResourceAsStream("Roboto-Bold.ttf")); + Font roboto = Font.createFont(Font.TRUETYPE_FONT, Paths.get(ForgeConstants.COMMON_FONTS_DIR, "Roboto-Bold.ttf").toFile()); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ge.registerFont(roboto); @@ -108,7 +110,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl attributes.put(TextAttribute.FAMILY, "Roboto Bold"); attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); - attributes.put(TextAttribute.SIZE, 10); + attributes.put(TextAttribute.SIZE, 11); attributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); smallCounterFont = Font.getFont(attributes); @@ -437,7 +439,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl } - if (card.getCounters() != null) { + if (card.getCounters() != null && !card.getCounters().isEmpty()) { switch (CounterDisplayType.from(FModel.getPreferences().getPref(FPref.UI_CARD_COUNTER_DISPLAY_TYPE))) { case OLD_WHEN_SMALL: @@ -517,7 +519,13 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl final int numberOfCounters = counterEntry.getValue(); final int counterBoxRealWidth = counterBoxBaseWidth + largeFontMetrics.stringWidth(String.valueOf(numberOfCounters)); - final int counterYOffset = cardYOffset + spaceFromTopOfCard - counterBoxHeight + currentCounter++ * (counterBoxHeight + counterBoxSpacing); + final int counterYOffset; + + if (ForgeConstants.CounterDisplayLocation.from(FModel.getPreferences().getPref(FPref.UI_CARD_COUNTER_DISPLAY_LOCATION)) == ForgeConstants.CounterDisplayLocation.TOP) { + counterYOffset = cardYOffset + spaceFromTopOfCard - counterBoxHeight + currentCounter++ * (counterBoxHeight + counterBoxSpacing); + } else { + counterYOffset = cardYOffset + cardHeight - spaceFromTopOfCard / 2 - counterBoxHeight + currentCounter++ * (counterBoxHeight + counterBoxSpacing); + } if (isSelected) { g.setColor(new Color(0, 0, 0, 255)); @@ -537,7 +545,8 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl } Rectangle nameBounds = counterArea.getBounds(); - nameBounds.x += 12; + nameBounds.x += 8; + nameBounds.y -= 1; nameBounds.width = 43; drawVerticallyCenteredString(g, counter.getCounterOnCardDisplayName(), nameBounds, smallCounterFont, smallFontMetrics); diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index aca4550f502..69031dba027 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -1,15 +1,13 @@ package forge; -import java.util.Stack; - import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.math.Matrix4; @@ -17,13 +15,14 @@ import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack; - import forge.assets.FImage; import forge.assets.FSkinColor; import forge.assets.FSkinFont; import forge.toolbox.FDisplayObject; import forge.util.Utils; +import java.util.Stack; + public class Graphics { private static final int GL_BLEND = GL20.GL_BLEND; private static final int GL_LINE_SMOOTH = 2848; //create constant here since not in GL20 @@ -62,6 +61,10 @@ public class Graphics { shapeRenderer.dispose(); } + public SpriteBatch getBatch() { + return batch; + } + public boolean startClip() { return startClip(0, 0, bounds.width, bounds.height); } @@ -138,7 +141,7 @@ public class Graphics { } else if (displayObj.getRotate180()) { //use center of bounds as pivot point startRotateTransform(displayObj.getWidth() / 2, displayObj.getHeight() / 2, 180); - //screen position won't change for this object from a 180 degree rotation + //screen position won't change for this object from a 180 degree rotation } displayObj.draw(this); @@ -546,10 +549,10 @@ public class Graphics { public void drawRepeatingImage(Texture image, float x, float y, float w, float h) { if (startClip(x, y, w, h)) { //only render if clip successful, otherwise it will escape bounds int tilesW = (int)(w / image.getWidth()) + 1; - int tilesH = (int)(h / image.getHeight()) + 1; + int tilesH = (int)(h / image.getHeight()) + 1; batch.draw(image, adjustX(x), adjustY(y, h), - image.getWidth() * tilesW, - image.getHeight() * tilesH, + image.getWidth() * tilesW, + image.getHeight() * tilesH, 0, tilesH, tilesW, 0); } endClip(); @@ -617,7 +620,7 @@ public class Graphics { else { textBounds = font.getMultiLineBounds(text); } - + boolean needClip = false; while (textBounds.width > w || textBounds.height > h) { @@ -667,11 +670,11 @@ public class Graphics { drawText(text, skinFont, textColor, x, y, w, h, wrap, horzAlignment, centerVertically); } - private float adjustX(float x) { + public float adjustX(float x) { return x + bounds.x; } - private float adjustY(float y, float height) { + public float adjustY(float y, float height) { return regionHeight - y - bounds.y - height; //flip y-axis } -} \ No newline at end of file +} diff --git a/forge-gui-mobile/src/forge/assets/FSkinFont.java b/forge-gui-mobile/src/forge/assets/FSkinFont.java index 789641030b7..13ddcfbd7df 100644 --- a/forge-gui-mobile/src/forge/assets/FSkinFont.java +++ b/forge-gui-mobile/src/forge/assets/FSkinFont.java @@ -1,9 +1,5 @@ package forge.assets; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; @@ -20,12 +16,15 @@ import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; import com.badlogic.gdx.graphics.glutils.PixmapTextureData; import com.badlogic.gdx.utils.Array; - import forge.FThreads; import forge.properties.ForgeConstants; import forge.util.FileUtil; import forge.util.Utils; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + public class FSkinFont { private static final int MIN_FONT_SIZE = 8; private static final int MAX_FONT_SIZE = 72; @@ -127,8 +126,7 @@ public class FSkinFont { font.setColor(color); if (wrap) { font.drawWrapped(batch, text, x, y, w, horzAlignment); - } - else { + } else { font.drawMultiLine(batch, text, x, y, w, horzAlignment); } } @@ -152,8 +150,7 @@ public class FSkinFont { if (scale != 1) { //re-use font inside range if possible if (fontSize > MAX_FONT_SIZE) { font = _get(MAX_FONT_SIZE).font; - } - else { + } else { font = _get(MIN_FONT_SIZE).font; } return; @@ -169,9 +166,7 @@ public class FSkinFont { font = new BitmapFont(data, (TextureRegion)null, true); } }); - return; - } - else { + } else { generateFont(FSkin.getSkinFile(TTF_FILE), fontName, fontSize); } } @@ -226,7 +221,7 @@ public class FSkinFont { if (pixmapDir != null) { FileHandle fontFile = pixmapDir.child(fontName + ".fnt"); BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text); - + String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, fontName); BitmapFontWriter.writeFont(font.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo(fontName, fontSize), 1, 1); } diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java index fcbad7ffe79..911f7673b5e 100644 --- a/forge-gui-mobile/src/forge/card/CardRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardRenderer.java @@ -1,39 +1,46 @@ package forge.card; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; - +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; +import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; +import com.badlogic.gdx.graphics.g2d.PixmapPacker; import com.badlogic.gdx.graphics.g2d.TextureRegion; - +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; +import com.badlogic.gdx.graphics.glutils.PixmapTextureData; +import com.badlogic.gdx.utils.Array; +import forge.FThreads; import forge.Graphics; import forge.StaticData; -import forge.assets.FImageComplex; -import forge.assets.FRotatedImage; -import forge.assets.FSkinColor; -import forge.assets.FSkinFont; -import forge.assets.FSkinImage; -import forge.assets.FTextureRegionImage; -import forge.assets.ImageCache; +import forge.assets.*; import forge.card.CardDetailUtil.DetailColors; import forge.card.CardZoom.ActivateHandler; import forge.card.mana.ManaCost; import forge.game.card.Card; import forge.game.card.CardView; import forge.game.card.CardView.CardStateView; +import forge.game.card.CounterType; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.model.FModel; +import forge.properties.ForgeConstants; +import forge.properties.ForgeConstants.CounterDisplayType; import forge.properties.ForgePreferences.FPref; import forge.screens.match.MatchController; import forge.toolbox.FList; import forge.util.Utils; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class CardRenderer { public enum CardStackPosition { @@ -53,6 +60,16 @@ public class CardRenderer { private static final float BORDER_THICKNESS = Utils.scale(1); public static final float PADDING_MULTIPLIER = 0.021f; + private static BitmapFont counterFont; + private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f); + private static final Map counterColorCache = new HashMap<>(); + + static { + if (counterFont == null) { + generateFontForCounters(); + } + } + private static Color fromDetailColor(DetailColors detailColor) { return FSkinColor.fromRGB(detailColor.r, detailColor.g, detailColor.b); } @@ -88,10 +105,12 @@ public class CardRenderer { CardType type = pc.getRules().getType(); return getCardArt(pc.getImageKey(false), pc.getRules().getSplitType() == CardSplitType.Split, type.isPlane() || type.isPhenomenon(),pc.getRules().getOracleText().contains("Aftermath")); } + public static FImageComplex getCardArt(CardView card) { CardTypeView type = card.getCurrentState().getType(); return getCardArt(card.getCurrentState().getImageKey(), card.isSplitCard(), type.isPlane() || type.isPhenomenon(),card.getText().contains("Aftermath")); } + public static FImageComplex getCardArt(String imageKey, boolean isSplitCard, boolean isHorizontalCard, boolean isAftermathCard) { FImageComplex cardArt = cardArtCache.get(imageKey); if (cardArt == null) { @@ -181,8 +200,6 @@ public class CardRenderer { return cardArt; } - - public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, CardView card, int count, String suffix, float x, float y, float w, float h, boolean compactMode) { final CardStateView state = card.getCurrentState(); if (card.getId() > 0) { @@ -201,6 +218,7 @@ public class CardRenderer { g.drawText(name, font, foreColor, x, y, w, h, false, HAlignment.CENTER, true); } } + public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, IPaperCard pc, int count, String suffix, float x, float y, float w, float h, boolean compactMode) { final CardView card = CardView.getCardForUi(pc); final CardStateView state = card.getCurrentState(); @@ -208,6 +226,7 @@ public class CardRenderer { pc.getRarity(), state.getPower(), state.getToughness(), state.getLoyalty(), count, suffix, x, y, w, h, compactMode); } + public static void drawCardListItem(Graphics g, FSkinFont font, FSkinColor foreColor, FImageComplex cardArt, CardView card, String set, CardRarity rarity, int power, int toughness, int loyalty, int count, String suffix, float x, float y, float w, float h, boolean compactMode) { float cardArtHeight = h + 2 * FList.PADDING; float cardArtWidth = cardArtHeight * CARD_ART_RATIO; @@ -295,6 +314,7 @@ public class CardRenderer { } return false; } + public static boolean paperCardListItemTap(List cards, int selectedIndex, ActivateHandler activateHandler, float x, float y, int count, boolean compactMode) { float cardArtHeight = getCardListItemHeight(compactMode); float cardArtWidth = cardArtHeight * CARD_ART_RATIO; @@ -337,6 +357,7 @@ public class CardRenderer { g.fillRect(Color.BLACK, x, y, w, h); } } + public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) { Texture image = ImageCache.getImage(card); if (image != null) { @@ -406,36 +427,28 @@ public class CardRenderer { g.drawOutlinedText(String.valueOf(card.getId()), idFont, Color.WHITE, Color.BLACK, x + padding, y + h - idHeight - padding, w, h, false, HAlignment.LEFT, false); } - int number = 0; - if (card.getCounters() != null) { - for (final Integer i : card.getCounters().values()) { - number += i.intValue(); + if (card.getCounters() != null && !card.getCounters().isEmpty()) { + + switch (CounterDisplayType.from(FModel.getPreferences().getPref(FPref.UI_CARD_COUNTER_DISPLAY_TYPE))) { + case OLD_WHEN_SMALL: + case TEXT: + drawCounterTabs(card, g, x, y, w, h); + break; + case IMAGE: + drawCounterImage(card, g, x, y, w, h); + break; + case HYBRID: + drawCounterImage(card, g, x, y, w, h); + drawCounterTabs(card, g, x, y, w, h); + break; } + } - final int counters = number; - - float countersSize = w / 2; - final float xCounters = x - countersSize / 2; - final float yCounters = y + h * 2 / 3 - countersSize; - - if (counters == 1) { - CardFaceSymbols.drawSymbol("counters1", g, xCounters, yCounters, countersSize, countersSize); - } - else if (counters == 2) { - CardFaceSymbols.drawSymbol("counters2", g, xCounters, yCounters, countersSize, countersSize); - } - else if (counters == 3) { - CardFaceSymbols.drawSymbol("counters3", g, xCounters, yCounters, countersSize, countersSize); - } - else if (counters > 3) { - CardFaceSymbols.drawSymbol("countersMulti", g, xCounters, yCounters, countersSize, countersSize); - } - - float otherSymbolsSize = w / 2; - final float combatXSymbols = (x + (w / 4)) - otherSymbolsSize / 2; - final float stateXSymbols = (x + (w / 2)) - otherSymbolsSize / 2; - final float ySymbols = (y + h) - (h / 8) - otherSymbolsSize / 2; + float otherSymbolsSize = w / 3.5f; + final float combatXSymbols = (x + (w / 4)) - otherSymbolsSize / 2 - 10; + final float stateXSymbols = (x + (w / 2)) - otherSymbolsSize / 2 - 10; + final float ySymbols = (y + h) - (h / 12) - otherSymbolsSize / 2; if (card.isAttacking()) { CardFaceSymbols.drawSymbol("attack", g, combatXSymbols, ySymbols, otherSymbolsSize, otherSymbolsSize); @@ -462,6 +475,111 @@ public class CardRenderer { //only needed if on top since otherwise P/T will be hidden drawPtBox(g, card, details, color, x, y, w, h); } + + } + + private static void drawCounterTabs(final CardView card, final Graphics g, final float x, final float y, final float w, final float h) { + + float otherSymbolsSize = w / 3.5f; + final float ySymbols = (h / 12) - otherSymbolsSize / 2; + + final float counterBoxHeight = 20; + final float counterBoxBaseWidth = 50; + final float counterBoxSpacing = -4; + + final float spaceFromTopOfCard = y + h - counterBoxHeight - counterBoxSpacing - otherSymbolsSize + ySymbols; + + int currentCounter = 0; + + if (CounterDisplayType.from(FModel.getPreferences().getPref(FPref.UI_CARD_COUNTER_DISPLAY_TYPE)) == CounterDisplayType.OLD_WHEN_SMALL) { + + int maxCounters = 0; + for (Integer numberOfCounters : card.getCounters().values()) { + maxCounters = Math.max(maxCounters, numberOfCounters); + } + + if (counterBoxBaseWidth + counterFont.getBounds(String.valueOf(maxCounters)).width > w) { + drawCounterImage(card, g, x, y, w, h); + return; + } + + } + + for (Map.Entry counterEntry : card.getCounters().entrySet()) { + + final CounterType counter = counterEntry.getKey(); + final int numberOfCounters = counterEntry.getValue(); + final float counterBoxRealWidth = counterBoxBaseWidth + counterFont.getBounds(String.valueOf(numberOfCounters)).width + 4; + + final float counterYOffset = spaceFromTopOfCard - (currentCounter++ * (counterBoxHeight + counterBoxSpacing)); + + g.fillRect(counterBackgroundColor, x - 2, counterYOffset, counterBoxRealWidth, counterBoxHeight); + + if (!counterColorCache.containsKey(counter)) { + counterColorCache.put(counter, new Color(counter.getRed() / 255.0f, counter.getGreen() / 255.0f, counter.getBlue() / 255.0f, 1.0f)); + } + + Color counterColor = counterColorCache.get(counter); + + drawText(g, counter.getCounterOnCardDisplayName(), counterFont, counterColor, x + 2, counterYOffset, counterBoxRealWidth, counterBoxHeight, HAlignment.LEFT); + drawText(g, String.valueOf(numberOfCounters), counterFont, counterColor, x + counterBoxBaseWidth - 3f, counterYOffset, counterBoxRealWidth, counterBoxHeight, HAlignment.LEFT); + + } + + } + + private static final int GL_BLEND = GL20.GL_BLEND; + + private static void drawText(Graphics g, String text, BitmapFont font, Color color, float x, float y, float w, float h, HAlignment horizontalAlignment) { + + if (color.a < 1) { //enable blending so alpha colored shapes work properly + Gdx.gl.glEnable(GL_BLEND); + } + + TextBounds textBounds = font.getMultiLineBounds(text); + + float textHeight = textBounds.height; + if (h > textHeight) { + y += (h - textHeight) / 2; + } + + font.setColor(color); + font.drawMultiLine(g.getBatch(), text, g.adjustX(x), g.adjustY(y, 0), w, horizontalAlignment); + + if (color.a < 1) { + Gdx.gl.glDisable(GL_BLEND); + } + + } + + private static void drawCounterImage(final CardView card, final Graphics g, final float x, final float y, final float w, final float h) { + + int number = 0; + if (card.getCounters() != null) { + for (final Integer i : card.getCounters().values()) { + number += i; + } + } + + final int counters = number; + + float countersSize = w / 2; + final float xCounters = x - countersSize / 2; + final float yCounters = y + h * 2 / 3 - countersSize; + + if (counters == 1) { + CardFaceSymbols.drawSymbol("counters1", g, xCounters, yCounters, countersSize, countersSize); + } + else if (counters == 2) { + CardFaceSymbols.drawSymbol("counters2", g, xCounters, yCounters, countersSize, countersSize); + } + else if (counters == 3) { + CardFaceSymbols.drawSymbol("counters3", g, xCounters, yCounters, countersSize, countersSize); + } + else if (counters > 3) { + CardFaceSymbols.drawSymbol("countersMulti", g, xCounters, yCounters, countersSize, countersSize); + } + } private static void drawPtBox(Graphics g, CardView card, CardStateView details, Color color, float x, float y, float w, float h) { @@ -564,4 +682,66 @@ public class CardRenderer { private static boolean showCardIdOverlay(CardView card) { return card.getId() > 0 && isShowingOverlays(card) && isPreferenceEnabled(FPref.UI_OVERLAY_CARD_ID); } + + //TODO Make FSkinFont accept more than one kind of font and merge this with it + private static void generateFontForCounters() { + + FileHandle ttfFile = Gdx.files.absolute(ForgeConstants.COMMON_FONTS_DIR).child("Roboto-Bold.ttf"); + final int fontSize = 11; + + if (!ttfFile.exists()) { return; } + + final FreeTypeFontGenerator generator = new FreeTypeFontGenerator(ttfFile); + + //approximate optimal page size + int pageSize = 128; + + //only generate images for characters that could be used by Forge + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*\u2014\u2022"; + + final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false); + final FreeTypeFontParameter parameter = new FreeTypeFontParameter(); + parameter.characters = chars; + parameter.size = fontSize; + parameter.packer = packer; + final FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(parameter); + final Array pages = packer.getPages(); + + //finish generating font on UI thread + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override + public void run() { + TextureRegion[] textureRegions = new TextureRegion[pages.size]; + for (int i = 0; i < pages.size; i++) { + PixmapPacker.Page p = pages.get(i); + Texture texture = new Texture(new PixmapTextureData(p.getPixmap(), p.getPixmap().getFormat(), false, false)) { + @Override + public void dispose() { + super.dispose(); + getTextureData().consumePixmap().dispose(); + } + }; + texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest); + textureRegions[i] = new TextureRegion(texture); + } + + counterFont = new BitmapFont(fontData, textureRegions, true); + + //create .fnt and .png files for font + FileHandle pixmapDir = Gdx.files.absolute(ForgeConstants.FONTS_DIR); + if (pixmapDir != null) { + FileHandle fontFile = pixmapDir.child("Roboto-Bold.fnt"); + BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text); + + String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, "Roboto-Bold"); + BitmapFontWriter.writeFont(counterFont.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo("Roboto-Bold", fontSize), 1, 1); + } + + generator.dispose(); + packer.dispose(); + } + }); + + } + } diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index c112bcaea9b..6534597aedb 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -133,7 +133,7 @@ public class SettingsPage extends TabPage { lstSettings.addItem(new CustomSelectSetting(FPref.UI_AUTO_YIELD_MODE, "Auto-Yield", "Defines the granularity level of auto-yields (yield to each unique ability or to each unique card).", - new String[]{ForgeConstants.AUTO_YIELD_PER_ABILITY, ForgeConstants.AUTO_YIELD_PER_CARD}), + new String[]{ForgeConstants.AUTO_YIELD_PER_ABILITY, ForgeConstants.AUTO_YIELD_PER_CARD}), 1); //Random Deck Generation @@ -214,11 +214,19 @@ public class SettingsPage extends TabPage { "Detailed Card Color", "Displays the breakdown of the current color of cards in the card detail information panel.", new String[]{ - ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR, + ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR, ForgeConstants.DISP_CURRENT_COLORS_CHANGED, ForgeConstants.DISP_CURRENT_COLORS_MULTI_OR_CHANGED, ForgeConstants.DISP_CURRENT_COLORS_ALWAYS}), 4); + lstSettings.addItem(new CustomSelectSetting(FPref.UI_CARD_COUNTER_DISPLAY_TYPE, + "Counter Display Type", + "Selects the style of the in-game counter display for cards. Text-based is a new tab-like display on the cards. Image-based is the old counter image. Hybrid displays both at once.", + new String[]{ + ForgeConstants.CounterDisplayType.TEXT.getName(), ForgeConstants.CounterDisplayType.IMAGE.getName(), + ForgeConstants.CounterDisplayType.HYBRID.getName(), ForgeConstants.CounterDisplayType.OLD_WHEN_SMALL.getName()}), + 4); + //Card Overlays lstSettings.addItem(new BooleanSetting(FPref.UI_SHOW_CARD_OVERLAYS, "Show Card Overlays", diff --git a/forge-gui-desktop/src/main/resources/Roboto-Bold.ttf b/forge-gui/res/fonts/Roboto-Bold.ttf similarity index 100% rename from forge-gui-desktop/src/main/resources/Roboto-Bold.ttf rename to forge-gui/res/fonts/Roboto-Bold.ttf diff --git a/forge-gui/src/main/java/forge/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/properties/ForgeConstants.java index dba36566919..aa725f01e3d 100644 --- a/forge-gui/src/main/java/forge/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/properties/ForgeConstants.java @@ -80,8 +80,9 @@ public final class ForgeConstants { private static final String CONQUEST_DIR = RES_DIR + "conquest" + PATH_SEPARATOR; public static final String CONQUEST_PLANES_DIR = CONQUEST_DIR + "planes" + PATH_SEPARATOR; - public static final String SKINS_DIR = RES_DIR + "skins" + PATH_SEPARATOR; - public static final String DEFAULT_SKINS_DIR = SKINS_DIR + "default" + PATH_SEPARATOR; + public static final String SKINS_DIR = RES_DIR + "skins" + PATH_SEPARATOR; + public static final String COMMON_FONTS_DIR = RES_DIR + "fonts" + PATH_SEPARATOR; + public static final String DEFAULT_SKINS_DIR = 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"; @@ -203,6 +204,31 @@ public final class ForgeConstants { public static final String AUTO_YIELD_PER_CARD = "Per Card (Each Game)"; public static final String AUTO_YIELD_PER_ABILITY = "Per Ability (Each Match)"; + public enum CounterDisplayLocation { + + TOP("Top of Card"), BOTTOM("Bottom of Card"); + + private String name; + + CounterDisplayLocation(final String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static CounterDisplayLocation from(final String name) { + for (CounterDisplayLocation counterDisplayLocation : values()) { + if (counterDisplayLocation.name.equals(name)) { + return counterDisplayLocation; + } + } + throw new IllegalArgumentException("Counter display location '" + name + "' not found."); + } + + } + public enum CounterDisplayType { /** Use only the new tab-like counter display */ diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java index ce73c98a5b8..48e4e5f8821 100644 --- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java @@ -92,6 +92,7 @@ public class ForgePreferences extends PreferencesStore { UI_AUTO_YIELD_MODE (ForgeConstants.AUTO_YIELD_PER_ABILITY), UI_SHOW_STORM_COUNT_IN_PROMPT ("false"), UI_CARD_COUNTER_DISPLAY_TYPE(ForgeConstants.CounterDisplayType.TEXT.getName()), + UI_CARD_COUNTER_DISPLAY_LOCATION(ForgeConstants.CounterDisplayLocation.TOP.getName()), UI_FOR_TOUCHSCREN("false"),