From 3b4c417549b3967740e5542048d87b480245ff25 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Tue, 29 Jul 2025 19:42:01 +0800 Subject: [PATCH] Refactor Foil Effect for Mobile Should fix rendering for Full, Crop and Art renders --- .../src/main/java/forge/ai/GameState.java | 9 +- .../src/main/java/forge/game/card/Card.java | 12 +- .../main/java/forge/game/card/CardView.java | 10 +- .../forge/trackable/TrackableProperty.java | 3 +- forge-gui-mobile/src/forge/Forge.java | 14 +- forge-gui-mobile/src/forge/Graphics.java | 86 +++++++++-- forge-gui-mobile/src/forge/GuiMobile.java | 2 +- forge-gui-mobile/src/forge/Shaders.java | 60 ++++++++ forge-gui-mobile/src/forge/assets/Assets.java | 9 ++ forge-gui-mobile/src/forge/assets/FSkin.java | 1 + .../src/forge/card/CardImage.java | 2 +- .../src/forge/card/CardImageRenderer.java | 29 ++-- .../src/forge/card/CardRenderer.java | 136 +++++------------- .../src/forge/screens/match/MatchScreen.java | 46 +++--- .../src/forge/toolbox/FChoiceList.java | 36 +---- .../src/forge/util/CardRendererUtils.java | 119 +++++++++++++++ forge-gui/res/skins/default/holofoil.png | Bin 0 -> 53288 bytes 17 files changed, 387 insertions(+), 187 deletions(-) create mode 100644 forge-gui-mobile/src/forge/util/CardRendererUtils.java create mode 100644 forge-gui/res/skins/default/holofoil.png diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index c8ba4c84cf5..92fdcd5a75e 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -264,12 +264,14 @@ public abstract class GameState { } if (c.hasMergedCard()) { + String suffix = c.getTopMergedCard().hasPaperFoil() ? "+" : ""; // we have to go by the current top card name here - newText.append(c.getTopMergedCard().getPaperCard().getName()).append("|Set:") + newText.append(c.getTopMergedCard().getPaperCard().getName()).append(suffix).append("|Set:") .append(c.getTopMergedCard().getPaperCard().getEdition()).append("|Art:") .append(c.getTopMergedCard().getPaperCard().getArtIndex()); } else { - newText.append(c.getPaperCard().getName()).append("|Set:").append(c.getPaperCard().getEdition()) + String suffix = c.hasPaperFoil() ? "+" : ""; + newText.append(c.getPaperCard().getName()).append(suffix).append("|Set:").append(c.getPaperCard().getEdition()) .append("|Art:").append(c.getPaperCard().getArtIndex()); } } @@ -326,8 +328,9 @@ public abstract class GameState { } else if (c.getCurrentStateName().equals(CardStateName.Meld)) { newText.append("|Meld"); if (c.getMeldedWith() != null) { + String suffix = c.getMeldedWith().hasPaperFoil() ? "+" : ""; newText.append(":"); - newText.append(c.getMeldedWith().getName()); + newText.append(c.getMeldedWith().getName()).append(suffix); } } else if (c.getCurrentStateName().equals(CardStateName.Modal)) { newText.append("|Modal"); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 73cb4c124c9..6c676e98f73 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -410,8 +410,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr view.updateSickness(this); view.updateClassLevel(this); view.updateDraftAction(this); - if (paperCard != null) + if (paperCard != null) { setMarkedColors(paperCard.getMarkedColors()); + setPaperFoil(paperCard.isFoil()); + } } public int getHiddenId() { @@ -2246,6 +2248,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr public boolean hasChosenColor(String s) { return chosenColors != null && chosenColors.contains(s); } + + public final boolean hasPaperFoil() { + return view.hasPaperFoil(); + } + public final void setPaperFoil(final boolean v) { + view.updatePaperFoil(v); + } + public final ColorSet getMarkedColors() { if (markedColor == null) { return ColorSet.getNullColor(); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 5bf402b30f8..cdb0f16d5cf 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -429,6 +429,12 @@ public class CardView extends GameEntityView { void updateChosenColors(Card c) { set(TrackableProperty.ChosenColors, c.getChosenColors()); } + public boolean hasPaperFoil() { + return get(TrackableProperty.PaperFoil); + } + void updatePaperFoil(boolean v) { + set(TrackableProperty.PaperFoil, v); + } public ColorSet getMarkedColors() { return get(TrackableProperty.MarkedColors); } @@ -1520,8 +1526,8 @@ public class CardView extends GameEntityView { public boolean hasPrintedPT() { return get(TrackableProperty.HasPrintedPT); } - void updateHasPrintedPT(boolean val) { - set(TrackableProperty.HasPrintedPT, val); + void updateHasPrintedPT(boolean v) { + set(TrackableProperty.HasPrintedPT, v); } public String getSetCode() { diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 0068a966f2c..5cef4898c2c 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -37,6 +37,7 @@ public enum TrackableProperty { Secondary(TrackableTypes.BooleanType), DoubleFaced(TrackableTypes.BooleanType), FacedownImageKey(TrackableTypes.StringType), + PaperFoil(TrackableTypes.BooleanType), //TODO? Cloner(TrackableTypes.StringType), @@ -137,7 +138,7 @@ public enum TrackableProperty { AttractionLights(TrackableTypes.IntegerSetType), ChangedColorWords(TrackableTypes.StringMapType), HasChangedColors(TrackableTypes.BooleanType), - HasPrintedPT(TrackableTypes.BooleanType, FreezeMode.IgnoresFreeze), + HasPrintedPT(TrackableTypes.BooleanType), ChangedTypes(TrackableTypes.StringMapType), //check produce mana for BG diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 3317ba0eb12..84ebb47f81e 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -78,6 +78,7 @@ public class Forge implements ApplicationListener { public static KeyInputAdapter keyInputAdapter; private static boolean exited, initialized; public boolean needsUpdate = false; + public static boolean switchClassic = false; public static boolean advStartup = false; public static boolean safeToClose = true; public static boolean magnify = false; @@ -91,6 +92,7 @@ public class Forge implements ApplicationListener { public static String extrawide = "default"; public static float heigtModifier = 0.0f; public static float deltaTime = 0f; + public static float hueFragTime = 0f; private static boolean isloadingaMatch = false; public static boolean autoAIDeckSelection = false; public static boolean showFPS = false; @@ -370,8 +372,8 @@ public class Forge implements ApplicationListener { Config.instance().loadResources(); SpellSmithScene.instance().loadEditions(); GameHUD.getInstance().stopAudio(); + MusicPlaylist.invalidateMusicPlaylist(); if (startScene) { - MusicPlaylist.invalidateMusicPlaylist(); SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); switchScene(StartScene.instance()); } @@ -728,6 +730,7 @@ public class Forge implements ApplicationListener { } } deltaTime = 0f; + hueFragTime = 0f; } } }); @@ -768,6 +771,9 @@ public class Forge implements ApplicationListener { } public static void switchToClassic() { + if (switchClassic) + return; + switchClassic = true; setTransitionScreen(new TransitionScreen(() -> { ImageCache.getInstance().disposeTextures(); isMobileAdventureMode = false; @@ -778,7 +784,8 @@ public class Forge implements ApplicationListener { clearTransitionScreen(); openHomeDefault(); exited = false; - }, Forge.takeScreenshot(), false, false)); + switchClassic = false; + }, takeScreenshot(), false, false)); } public static void switchToAdventure() { @@ -870,6 +877,9 @@ public class Forge implements ApplicationListener { deltaTime += Gdx.graphics.getDeltaTime(); if (deltaTime > 22.5f) deltaTime = 0f; + hueFragTime += Gdx.graphics.getDeltaTime(); + if (hueFragTime > 6.29f) + hueFragTime = 0f; FContainer screen = currentScreen; diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index 0ad99284545..177ebfe71e8 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -55,6 +55,7 @@ public class Graphics { private final ShaderProgram shaderChromaticAbberation = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragChromaticAbberation); private final ShaderProgram shaderHueShift = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragHueShift); private final ShaderProgram shaderRoundedRect = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragRoundedRect); + private final ShaderProgram shaderRoundedRect2 = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragRoundedRect2); private final ShaderProgram shaderNoiseFade = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragNoiseFade); private final ShaderProgram shaderPortal = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragPortal); private final ShaderProgram shaderPixelateSimple = new ShaderProgram(Shaders.vertPixelateShader, Shaders.fragPixelateSimple); @@ -106,13 +107,28 @@ public class Graphics { } public void dispose() { - batch.dispose(); - shapeRenderer.dispose(); - shaderOutline.dispose(); - shaderGrayscale.dispose(); - shaderUnderwater.dispose(); - shaderWarp.dispose(); - if (dummyTexture != null) dummyTexture.dispose(); + try { + batch.dispose(); + } catch (Exception ignored) {} + try { + shapeRenderer.dispose(); + } catch (Exception ignored) {} + try { + shaderOutline.dispose(); + } catch (Exception ignored) {} + try { + shaderGrayscale.dispose(); + } catch (Exception ignored) {} + try { + shaderUnderwater.dispose(); + } catch (Exception ignored) {} + try { + shaderWarp.dispose(); + } catch (Exception ignored) {} + try { + if (dummyTexture != null) + dummyTexture.dispose(); + } catch (Exception ignored) {} } public Batch getBatch() { @@ -918,13 +934,40 @@ public class Graphics { batch.begin(); } - public void drawCardRoundRect(Texture image, TextureRegion damage_overlay, float x, float y, float w, float h, boolean drawGray, boolean damaged) { + public void drawFoil(float x, float y, float w, float h, float radius) { + drawFoil(x, y, w, h, radius, false); + } + + public void drawFoil(float x, float y, float w, float h, float radius, boolean rotate) { + Texture image = Forge.getAssets().getHolofoil(); if (image == null) return; batch.end(); + shaderRoundedRect2.bind(); + shaderRoundedRect2.setUniformf("u_resolution", image.getWidth(), image.getHeight()); + shaderRoundedRect2.setUniformf("edge_radius", (float)(image.getHeight() / image.getWidth()) * radius); + shaderRoundedRect2.setUniformf("u_time", Forge.hueFragTime); + batch.setShader(shaderRoundedRect2); + batch.begin(); + //draw + if (rotate) + drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, 0, 0, image.getWidth(), image.getHeight(), 90); + else + batch.draw(image, adjustX(x), adjustY(y, h), w, h); + //reset + batch.end(); + batch.setShader(null); + batch.begin(); + } + + public void drawCardRoundRect(Texture image, TextureRegion damage_overlay, float x, float y, float w, float h, boolean drawGray, boolean damaged, boolean foilEffect) { + if (image == null) + return; + float radius = ImageCache.getInstance().getRadius(image); + batch.end(); shaderRoundedRect.bind(); shaderRoundedRect.setUniformf("u_resolution", image.getWidth(), image.getHeight()); - shaderRoundedRect.setUniformf("edge_radius", (float)(image.getHeight() / image.getWidth()) * ImageCache.getInstance().getRadius(image)); + shaderRoundedRect.setUniformf("edge_radius", (float)(image.getHeight() / image.getWidth()) * radius); shaderRoundedRect.setUniformf("u_gray", drawGray ? 0.8f : 0f); batch.setShader(shaderRoundedRect); batch.begin(); @@ -934,22 +977,31 @@ public class Graphics { batch.end(); batch.setShader(null); batch.begin(); + if (foilEffect && !drawGray) { + drawFoil(x, y, w, h, radius); + } if (damage_overlay != null && damaged) batch.draw(damage_overlay, adjustX(x), adjustY(y, h), w, h); } public void drawCardRoundRect(Texture image, float x, float y, float w, float h, float originX, float originY, float rotation) { + drawCardRoundRect(image, x, y, w, h, originX, originY, rotation, 1f, false); + } + + public void drawCardRoundRect(Texture image, float x, float y, float w, float h, float originX, float originY, float rotation, float modR, boolean drawFoil) { if (image == null) return; batch.end(); shaderRoundedRect.bind(); shaderRoundedRect.setUniformf("u_resolution", image.getWidth(), image.getHeight()); - shaderRoundedRect.setUniformf("edge_radius", (float)(image.getHeight() / image.getWidth()) * ImageCache.getInstance().getRadius(image)); + shaderRoundedRect.setUniformf("edge_radius", (float)(image.getHeight() / image.getWidth()) * (ImageCache.getInstance().getRadius(image) * modR)); shaderRoundedRect.setUniformf("u_gray", 0f); batch.setShader(shaderRoundedRect); batch.begin(); //draw drawRotatedImage(image, x, y, w, h, originX, originY, 0, 0, image.getWidth(), image.getHeight(), rotation); + if (drawFoil) + drawFoil(x, y, w, h, modR, true); //reset batch.end(); batch.setShader(null); @@ -1273,13 +1325,25 @@ public class Graphics { } public void drawImage(Texture image, float x, float y, float w, float h) { + drawImage(image, x, y, w, h, false); + } + + public void drawImage(Texture image, float x, float y, float w, float h, boolean drawFoil) { if (image != null) batch.draw(image, adjustX(x), adjustY(y, h), w, h); + if (drawFoil) + drawFoil(x, y, w, h, 0f); } public void drawImage(TextureRegion image, float x, float y, float w, float h) { + drawImage(image, x, y, w, h, false); + } + + public void drawImage(TextureRegion image, float x, float y, float w, float h, boolean drawFoil) { if (image != null) batch.draw(image, adjustX(x), adjustY(y, h), w, h); + if (drawFoil) + drawFoil(x, y, w, h, 0f); } public void drawImage(TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) { @@ -1509,7 +1573,7 @@ public class Graphics { } public Color borderLining(String c) { - if (c == null || c == "") + if (c == null || "".equals(c)) return Color.valueOf("#fffffd"); int c_r = Integer.parseInt(c.substring(0, 2), 16); int c_g = Integer.parseInt(c.substring(2, 4), 16); diff --git a/forge-gui-mobile/src/forge/GuiMobile.java b/forge-gui-mobile/src/forge/GuiMobile.java index 27e7d68408a..2af4a82bf0a 100644 --- a/forge-gui-mobile/src/forge/GuiMobile.java +++ b/forge-gui-mobile/src/forge/GuiMobile.java @@ -154,7 +154,7 @@ public class GuiMobile implements IGuiBase { } else if (paperCard != null) { Texture cardImage = ImageCache.getInstance().getImage(paperCard.getCardImageKey(), false); if (cardImage != null) - g.drawCardRoundRect(cardImage, null, (background.getWidth() - cardImageWidth) / 2, (background.getHeight() - cardImageHeight) / 3.8f, cardImageWidth, cardImageHeight, false, false); + g.drawCardRoundRect(cardImage, null, (background.getWidth() - cardImageWidth) / 2, (background.getHeight() - cardImageHeight) / 3.8f, cardImageWidth, cardImageHeight, false, false, paperCard.isFoil()); } Gdx.graphics.requestRendering(); //ensure image appears right away diff --git a/forge-gui-mobile/src/forge/Shaders.java b/forge-gui-mobile/src/forge/Shaders.java index 784517459b9..9e6dfdabe2c 100644 --- a/forge-gui-mobile/src/forge/Shaders.java +++ b/forge-gui-mobile/src/forge/Shaders.java @@ -181,6 +181,66 @@ public class Shaders { " }\n" + " gl_FragColor = col*alpha;\n" + "}"; + public static final String fragRoundedRect2 = "#ifdef GL_ES\n" + + "#define LOWP lowp\n" + + "precision mediump float;\n" + + "#else\n" + + "#define LOWP \n" + + "#endif\n" + + "varying vec2 v_texCoords;\n" + + "uniform sampler2D u_texture;\n" + + "uniform vec2 u_resolution;\n" + + "uniform float edge_radius;\n" + + "uniform float u_time;\n" + + "LOWP vec4 color = vec4(1.0,1.0,1.0,1.0);\n" + + "float gradientIntensity = 0.5;\n" + + "const float contrast = 1.5 ;\n" + + "vec3 barronSpline(vec3 x, float shape) {\n" + + " const float turning = 0.5;\n" + + " vec3 d = turning - x;\n" + + " return mix(\n" + + " ((1. - turning) * (x - 1.)) / (1. - (x + shape * d)) + 1.,\n" + + " (turning * x) / (1.0e-20 + (x + shape * d)),\n" + + " step(0.0, d));\n" + + "}\n" + + "\n" + + "vec3 hs(vec3 c, float s) {\n" + + " vec3 m=vec3(cos(s),s=sin(s)*.5774,-s);\n" + + " return c*mat3(m+=(1.-m.x)/3.,m.zxy,m.yzx);\n" + + "}\n" + + "vec3 applyHue(vec3 rgb, float hue)\n" + + "{\n" + + " //rgb = ((rgb + 0.5f) * 1f) - 0.5f;\n" + + " //rgb = barronSpline(rgb, contrast);\n" + + " vec3 k = vec3(0.5774);\n" + + " float c = cos(hue);\n" + + " //Rodrigues' rotation formula\n" + + " return rgb * c + cross(k, rgb) * sin(hue) + k * dot(k, rgb) * (1.0 - c);\n" + + "}\n"+ + "void main() {\n" + + " vec2 uv = v_texCoords;\n" + + " vec2 uv_base_center = uv * 2.0 - 1.0;\n" + + "\n" + + " vec2 half_resolution = u_resolution.xy * 0.5;\n" + + " vec2 abs_rounded_center = half_resolution.xy - edge_radius;\n" + + " vec2 abs_pixel_coord = vec2( abs(uv_base_center.x * half_resolution.x), abs(uv_base_center.y * half_resolution.y) );\n" + + "\n" + + " float alpha = 1.0;\n" + + " LOWP vec4 orig = color * texture2D(u_texture, uv);\n" + + " vec3 col = orig.rgb;\n" + + " vec4 col2 = vec4(applyHue(col, u_time), 1.);\n" + + " //multiply the original texture alpha to render only opaque shifted colors \n" + + " col2.a *= orig.a;\n" + + " uv.y = -1.0 - uv.y;" + + " uv.x += sin(uv.y*12.0+u_time)/4.0;" + + " if (abs_pixel_coord.x > abs_rounded_center.x && abs_pixel_coord.y > abs_rounded_center.y) {\n" + + " float r = length(abs_pixel_coord - abs_rounded_center);\n" + + " alpha = smoothstep(edge_radius, edge_radius - gradientIntensity, r);\n" + + " \n" + + " }\n" + + " //alpha here is the rounded edges to be removed \n" + + " gl_FragColor = col2*alpha;\n" + + "}"; public static final String fragHueShift = "#ifdef GL_ES\n" + "#define LOWP lowp\n" + "precision mediump float;\n" + diff --git a/forge-gui-mobile/src/forge/assets/Assets.java b/forge-gui-mobile/src/forge/assets/Assets.java index 65852e50749..c345730c2dc 100644 --- a/forge-gui-mobile/src/forge/assets/Assets.java +++ b/forge-gui-mobile/src/forge/assets/Assets.java @@ -30,6 +30,8 @@ import forge.localinstance.skin.FSkinProp; import java.util.HashMap; import java.util.Map; +import static forge.assets.FSkin.getDefaultSkinFile; + public class Assets implements Disposable { private MemoryTrackingAssetManager manager; private HashMap fonts; @@ -51,6 +53,7 @@ public class Assets implements Disposable { private TextureParameter textureParameter; private ObjectMap textrafonts; private int cFB = 0, cFBVal = 0, cTM = 0, cTMVal = 0, cSF = 0, cSFVal = 0, cCF = 0, cCFVal = 0; + private Texture holofoil; public Assets() { String titleFilename = Forge.isLandscapeMode() ? "title_bg_lq.png" : "title_bg_lq_portrait.png"; @@ -354,6 +357,12 @@ public class Assets implements Disposable { return dummy; } + public Texture getHolofoil() { + if (holofoil == null) { + holofoil = getTexture(getDefaultSkinFile("holofoil.png")); + } + return holofoil; + } public Font getTextraFont(BitmapFont bitmapFont, TextureAtlas item_atlas, TextureAtlas pixelmana_atlas) { if (textrafonts == null) textrafonts = new ObjectMap<>(); diff --git a/forge-gui-mobile/src/forge/assets/FSkin.java b/forge-gui-mobile/src/forge/assets/FSkin.java index 617cd78a98b..f8002076b12 100644 --- a/forge-gui-mobile/src/forge/assets/FSkin.java +++ b/forge-gui-mobile/src/forge/assets/FSkin.java @@ -180,6 +180,7 @@ public class FSkin { Forge.getAssets().loadTexture(getDefaultSkinFile("overlay_alpha.png")); Forge.getAssets().loadTexture(getDefaultSkinFile("spiral.png")); Forge.getAssets().loadTexture(getDefaultSkinFile("splatter.png")); + Forge.getAssets().loadTexture(getDefaultSkinFile("holofoil.png")); if (splashScreen != null) { final FileHandle f = getSkinFile("bg_splash.png"); diff --git a/forge-gui-mobile/src/forge/card/CardImage.java b/forge-gui-mobile/src/forge/card/CardImage.java index a354baca159..85d24429557 100644 --- a/forge-gui-mobile/src/forge/card/CardImage.java +++ b/forge-gui-mobile/src/forge/card/CardImage.java @@ -50,7 +50,7 @@ public class CardImage implements FImage { } else { if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, null, x, y, w, h, false, false); + g.drawCardRoundRect(image, null, x, y, w, h, false, false, false); else { float radius = (h - w) / 8; g.drawborderImage(ImageCache.getInstance().borderColor(image), x, y, w, h); diff --git a/forge-gui-mobile/src/forge/card/CardImageRenderer.java b/forge-gui-mobile/src/forge/card/CardImageRenderer.java index a6a4f2d912b..09c72856361 100644 --- a/forge-gui-mobile/src/forge/card/CardImageRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardImageRenderer.java @@ -10,8 +10,7 @@ import java.util.List; import forge.ImageKeys; import forge.assets.*; import forge.item.PaperCard; -import forge.util.ImageUtil; -import forge.util.TextBounds; +import forge.util.*; import org.apache.commons.lang3.StringUtils; import com.badlogic.gdx.graphics.Color; @@ -35,8 +34,6 @@ import forge.localinstance.properties.ForgePreferences; import forge.model.FModel; import forge.screens.FScreen; import forge.screens.match.MatchController; -import forge.util.CardTranslation; -import forge.util.Utils; public class CardImageRenderer { private static final float BASE_IMAGE_WIDTH = 360; @@ -791,6 +788,9 @@ public class CardImageRenderer { } } public static void drawZoom(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h, float dispW, float dispH, boolean isCurrentCard) { + drawZoom(g, card, gameView, altState, x, y, w, h, dispW, dispH, isCurrentCard, 1f); + } + public static void drawZoom(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h, float dispW, float dispH, boolean isCurrentCard, float modR) { boolean canshow = MatchController.instance.mayView(card); String key = card.getState(altState).getImageKey(); Texture image = new CachedCardImageRenderer(key).getImage(); @@ -814,33 +814,31 @@ public class CardImageRenderer { float new_h = h * wh_Adj; float new_x = ForgeConstants.isGdxPortLandscape && isCurrentCard ? (dispW - new_w) / 2 : x; float new_y = ForgeConstants.isGdxPortLandscape && isCurrentCard ? (dispH - new_h) / 2 : y; - float new_xRotate = (dispW - new_h) / 2; - float new_yRotate = (dispH - new_w) / 2; - boolean rotateSplit = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS); - boolean rotatePlane = FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON); float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f; float minusxy = isModernFrame(card) ? 0.0f : 0.13f * radius; if (card.getCurrentState().getSetCode().equals("LEA") || card.getCurrentState().getSetCode().equals("LEB")) { croppedArea = 0.975f; minusxy = 0.135f * radius; } - if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !altState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && altState))) { + if (canshow && CardRendererUtils.needsRotation(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON, card, altState)) { if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); else { g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), new_x + radius / 2 - minusxy, new_y + radius / 2 - minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2 - minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2 - minusxy) + (new_h * croppedArea) / 2, -90); + if (CardRendererUtils.drawFoil(card)) + g.drawFoil(new_x, new_y, new_w, new_h, modR, true); } } else if (Forge.enableUIMask.equals("Crop")) { g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); } else g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90); - } else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow && !card.isFaceDown()) { + } else if (canshow && CardRendererUtils.needsRotation(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS, card, altState)) { boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath"); if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); + g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90, modR, CardRendererUtils.drawFoil(card)); else { g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getInstance().getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90); g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), new_x + radius / 2 - minusxy, new_y + radius / 2 - minusxy, new_w * croppedArea, new_h * croppedArea, (new_x + radius / 2 - minusxy) + (new_w * croppedArea) / 2, (new_y + radius / 2 - minusxy) + (new_h * croppedArea) / 2, isAftermath ? 90 : -90); @@ -852,7 +850,7 @@ public class CardImageRenderer { } else { if (card.isFaceDown() && ZoneType.Exile.equals(card.getZone())) { if (card.isForeTold() || altState) { - if (card.isSplitCard() && rotateSplit && isCurrentCard) { + if (CardRendererUtils.needsRotation(ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS, card, altState) && isCurrentCard) { boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath"); if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) @@ -868,7 +866,7 @@ public class CardImageRenderer { } else { if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, null, x, y, w, h, false, false); + g.drawCardRoundRect(image, null, x, y, w, h, false, false, CardRendererUtils.drawFoil(card)); else { g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h); g.drawImage(ImageCache.getInstance().croppedBorderImage(image), x + radius / 2.4f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea); @@ -885,7 +883,7 @@ public class CardImageRenderer { } } else if (Forge.enableUIMask.equals("Full") && canshow) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, null, x, y, w, h, false, false); + g.drawCardRoundRect(image, null, x, y, w, h, false, false, CardRendererUtils.drawFoil(card)); else { g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h); g.drawImage(ImageCache.getInstance().croppedBorderImage(image), x + radius / 2.4f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea); @@ -900,7 +898,8 @@ public class CardImageRenderer { } } } - CardRenderer.drawFoilEffect(g, card, x, y, w, h, isCurrentCard && canshow && image != ImageCache.getInstance().getDefaultImage()); + if (canshow && !Forge.enableUIMask.equals("Full") && CardRendererUtils.drawFoil(card)) + g.drawFoil(x, y, w, h, 0f, CardRendererUtils.needsRotation(card, altState)); } public static void drawDetails(Graphics g, CardView card, GameView gameView, boolean altState, float x, float y, float w, float h) { diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java index 90bc944573e..0c04afa22a0 100644 --- a/forge-gui-mobile/src/forge/card/CardRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardRenderer.java @@ -53,7 +53,6 @@ import forge.gui.card.CardDetailUtil.DetailColors; import forge.item.IPaperCard; import forge.item.InventoryItem; import forge.localinstance.properties.ForgeConstants.CounterDisplayType; -import forge.localinstance.properties.ForgePreferences; import forge.localinstance.properties.ForgePreferences.FPref; import forge.localinstance.skin.FSkinProp; import forge.model.FModel; @@ -605,28 +604,29 @@ public class CardRenderer { croppedArea = 0.975f; minusxy = 0.135f * radius; } + if (pc.isFoil()) { //draw foil effect if needed + if (card.getCurrentState().getFoilIndex() == 0) { //if foil finish not yet established, assign a random one + card.getCurrentState().setFoilIndexOverride(-1); + } + } if (image != null) { if (image == ImageCache.getInstance().getDefaultImage() || Forge.enableUIMask.equals("Art")) { CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true); } else { if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, null, x, y, w, h, false, false); + g.drawCardRoundRect(image, null, x, y, w, h, false, false, CardRendererUtils.drawFoil(card)); else { //tint the border g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h); g.drawImage(ImageCache.getInstance().croppedBorderImage(image), x + radius / 2.4f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea); + if (CardRendererUtils.drawFoil(card)) + g.drawFoil(x, y, w, h, radius); } } else if (Forge.enableUIMask.equals("Crop")) { - g.drawImage(ImageCache.getInstance().croppedBorderImage(image), x, y, w, h); + g.drawImage(ImageCache.getInstance().croppedBorderImage(image), x, y, w, h, CardRendererUtils.drawFoil(card)); } else - g.drawImage(image, x, y, w, h); - } - if (pc.isFoil()) { //draw foil effect if needed - if (card.getCurrentState().getFoilIndex() == 0) { //if foil finish not yet established, assign a random one - card.getCurrentState().setFoilIndexOverride(-1); - } - drawFoilEffect(g, card, x, y, w, h, false); + g.drawImage(image, x, y, w, h, CardRendererUtils.drawFoil(card)); } } else { //if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically @@ -652,59 +652,56 @@ public class CardRenderer { minusxy = 0.135f * radius; } if (image != null) { + float cardR = ImageCache.getInstance().getRadius(image); if (image == ImageCache.getInstance().getDefaultImage() || Forge.enableUIMask.equals("Art")) { - CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, true, false, isChoiceList, !showCardIdOverlay(card)); + CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, true, false, isChoiceList, !CardRendererUtils.showCardIdOverlay(card)); } else if (showsleeves) { if (!card.isForeTold()) - g.drawCardImage(sleeves, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(sleeves, crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); else - g.drawCardImage(image, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(image, crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); } else { - if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON) - && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !showAltState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && showAltState)) && rotate) { + if (rotate) { + float rotation = CardRendererUtils.hasAftermath(card) ? 90 : -90; if (Forge.enableUIMask.equals("Full")) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, x, y, w, h, x + w / 2, y + h / 2, -90); + g.drawCardRoundRect(image, x, y, w, h, x + w / 2, y + h / 2, rotation); else { - g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90); - g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), x + radius / 2.3f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea, (x + radius / 2.3f - minusxy) + (w * croppedArea) / 2, (y + radius / 2 - minusxy) + (h * croppedArea) / 2, -90); + g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, rotation); + g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), x + radius / 2.3f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea, (x + radius / 2.3f - minusxy) + (w * croppedArea) / 2, (y + radius / 2 - minusxy) + (h * croppedArea) / 2, rotation); } + } else if (Forge.enableUIMask.equals("Crop")) { - g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), x, y, w, h, x + w / 2, y + h / 2, -90); + g.drawRotatedImage(ImageCache.getInstance().croppedBorderImage(image), x, y, w, h, x + w / 2, y + h / 2, rotation); } else - g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90); + g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, rotation); } else { if (Forge.enableUIMask.equals("Full") && canshow) { if (ImageCache.getInstance().isFullBorder(image)) - g.drawCardRoundRect(image, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardRoundRect(image, crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify), CardRendererUtils.drawFoil(card)); else { //boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors(); g.drawBorderImage(ImageCache.getInstance().getBorderImage(image.toString(), canshow), ImageCache.getInstance().borderColor(image), ImageCache.getInstance().getTint(card, image), x, y, w, h, false); //tint check for changed colors - g.drawCardImage(ImageCache.getInstance().croppedBorderImage(image), crack_overlay, x + radius / 2.4f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(ImageCache.getInstance().croppedBorderImage(image), crack_overlay, x + radius / 2.4f - minusxy, y + radius / 2 - minusxy, w * croppedArea, h * croppedArea, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); } } else if (Forge.enableUIMask.equals("Crop") && canshow) { - g.drawCardImage(ImageCache.getInstance().croppedBorderImage(image), crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(ImageCache.getInstance().croppedBorderImage(image), crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); } else { if (canshow) - g.drawCardImage(image, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(image, crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); else // draw card back sleeves - g.drawCardImage(sleeves, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0); + g.drawCardImage(sleeves, crack_overlay, x, y, w, h, CardRendererUtils.drawGray(card), CardRendererUtils.drawCracks(card, magnify)); } } } - drawFoilEffect(g, card, x, y, w, h, false); + if (canshow && CardRendererUtils.drawFoil(card)) + g.drawFoil(x, y, w, h, Forge.enableUIMask.equals("Full") ? cardR : 0f, !Forge.enableUIMask.equals("Art") && rotate); } else { //if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically - CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, true, false, isChoiceList, !showCardIdOverlay(card)); + CardImageRenderer.drawCardImage(g, card, showAltState, x, y, w, h, pos, true, false, isChoiceList, !CardRendererUtils.showCardIdOverlay(card)); } } - private static boolean drawGray(CardView c) { - if (c == null) - return false; - return c.wasDestroyed() || c.isPhasedOut(); - } - public static void drawCardWithOverlays(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos) { drawCardWithOverlays(g, card, x, y, w, h, pos, false, false, false); } @@ -757,7 +754,7 @@ public class CardRenderer { } } - if (canShow && showCardIdOverlay(card)) { + if (canShow && CardRendererUtils.showCardIdOverlay(card)) { FSkinFont idFont = FSkinFont.forHeight(h * 0.11f); float idHeight = idFont.getCapHeight(); g.drawOutlinedText(String.valueOf(card.getId()), idFont, Color.WHITE, Color.BLACK, x + padding, y + h - idHeight - padding, w, h, false, Align.left, false); @@ -805,7 +802,7 @@ public class CardRenderer { CardFaceSymbols.drawSymbol("sacrifice", g, (x + (w / 2)) - sacSymbolSize / 2, (y + (h / 2)) - sacSymbolSize / 2, otherSymbolsSize, otherSymbolsSize); } - if (onTop && showCardPowerOverlay(card) && (canShow || card.isFaceDown())) { //make sure card p/t box appears on top + if (onTop && CardRendererUtils.showCardPowerOverlay(card) && (canShow || card.isFaceDown())) { //make sure card p/t box appears on top //only needed if on top since otherwise P/T will be hidden drawPtBox(g, card, details, color, x, y, w, h); } @@ -822,8 +819,8 @@ public class CardRenderer { g.setAlphaComposite(0.6f); } if (ZoneType.Battlefield.equals(card.getZone()) && onTop) { - drawAbilityIcons(g, card, cx, cy, cw, ch, cx + ((cw * 2) / 2.3f), cy, cw / 5.5f, cw / 5.7f, showAbilityIcons(card)); - } else if (canShow && !ZoneType.Battlefield.equals(card.getZone()) && showAbilityIcons(card)) { + drawAbilityIcons(g, card, cx, cy, cw, ch, cx + ((cw * 2) / 2.3f), cy, cw / 5.5f, cw / 5.7f, CardRendererUtils.showAbilityIcons(card)); + } else if (canShow && !ZoneType.Battlefield.equals(card.getZone()) && CardRendererUtils.showAbilityIcons(card)) { //draw indicator for flash or can be cast at instant speed, enabled if show ability icons is enabled String keywordKey = card.getCurrentState().getKeywordKey(); String abilityText = card.getCurrentState().getAbilityText(); @@ -836,7 +833,7 @@ public class CardRenderer { } //draw name and mana cost overlays if card is small or default card image being used if (h <= NAME_COST_THRESHOLD && canShow) { - if (showCardNameOverlay(card)) { + if (CardRendererUtils.showCardNameOverlay(card)) { float multiplier; switch (Forge.extrawide) { case "default": @@ -854,7 +851,7 @@ public class CardRenderer { } g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * multiplier), Color.WHITE, Color.BLACK, cx + padding - 1f, cy + padding, cw - 2 * padding, ch * 0.4f, true, Align.left, false, true); } - if (showCardManaCostOverlay(card)) { + if (CardRendererUtils.showCardManaCostOverlay(card)) { float manaSymbolSize = w / 4.5f; if (card.isSplitCard() && card.hasAlternateState() && !card.isFaceDown() && card.getZone() != ZoneType.Stack && card.getZone() != ZoneType.Battlefield) { if (isChoiceList) { @@ -1442,67 +1439,6 @@ public class CardRenderer { CardFaceSymbols.drawManaCost(g, cost, x + (w - manaCostWidth) / 2, y + (h - manaSymbolSize) / 2, manaSymbolSize); } - public static void drawFoilEffect(Graphics g, CardView card, float x, float y, float w, float h, boolean inZoomer) { - if (card.getCurrentState().isBattle()) - return; - if (card.getAlternateState() != null && card.getCurrentState().isBattle()) - return; - //todo add support for battle, better to move the render inside the draw method for card in the future or a general foil effect shader.. - float new_x = x; - float new_y = y; - float new_w = w; - float new_h = h; - float radius = (h - w) / 8; - float croppedArea = isModernFrame(card) ? CROP_MULTIPLIER : 0.97f; - float minusxy = isModernFrame(card) ? 0.0f : 0.13f * radius; - if (card.getCurrentState().getSetCode().equals("LEA") || card.getCurrentState().getSetCode().equals("LEB")) { - croppedArea = 0.975f; - minusxy = 0.135f * radius; - } - if (Forge.enableUIMask.equals("Full")) { - new_x += radius / 2.4f - minusxy; - new_y += radius / 2 - minusxy; - new_w = w * croppedArea; - new_h = h * croppedArea; - } - if (isPreferenceEnabled(FPref.UI_OVERLAY_FOIL_EFFECT) && MatchController.instance.mayView(card)) { - boolean rotateSplit = isPreferenceEnabled(FPref.UI_ROTATE_SPLIT_CARDS) && card.isSplitCard() && inZoomer; - int foil = card.getCurrentState().getFoilIndex(); - if (foil > 0) { - CardFaceSymbols.drawOther(g, String.format("foil%02d", foil), new_x, new_y, new_w, new_h, rotateSplit); - } - } - } - - private static boolean isPreferenceEnabled(FPref preferenceName) { - return FModel.getPreferences().getPrefBoolean(preferenceName); - } - - private static boolean isShowingOverlays(CardView card) { - return isPreferenceEnabled(FPref.UI_SHOW_CARD_OVERLAYS) && card != null; - } - - private static boolean showCardNameOverlay(CardView card) { - return isShowingOverlays(card) && isPreferenceEnabled(FPref.UI_OVERLAY_CARD_NAME); - } - - private static boolean showCardPowerOverlay(CardView card) { - return isShowingOverlays(card) && isPreferenceEnabled(FPref.UI_OVERLAY_CARD_POWER); - } - - private static boolean showCardManaCostOverlay(CardView card) { - return isShowingOverlays(card) && - isPreferenceEnabled(FPref.UI_OVERLAY_CARD_MANA_COST); - } - - public static boolean showAbilityIcons(CardView card) { - return isShowingOverlays(card) && isPreferenceEnabled(FPref.UI_OVERLAY_ABILITY_ICONS); - } - - 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(final int fontSize) { diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index 2a5b7d6a8ea..2c1461285c7 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -18,6 +18,7 @@ import forge.gui.interfaces.IGuiGame; import forge.screens.match.views.VField; import forge.screens.match.views.VReveal; import forge.toolbox.FDisplayObject; +import forge.util.CardRendererUtils; import forge.util.Utils; import forge.util.collect.FCollectionView; import org.apache.commons.lang3.tuple.Pair; @@ -404,36 +405,43 @@ public class MatchScreen extends FScreen { if (object instanceof FCardPanel cardPanel) { try { if (cardPanel.isHovered()) { - VPlayerPanel vPlayerPanel = getPlayerPanel(cardPanel.getCard().getController()); + CardView cardView = cardPanel.getCard(); + VPlayerPanel vPlayerPanel = getPlayerPanel(cardView.getController()); if (vPlayerPanel == null) - vPlayerPanel = getPlayerPanel(cardPanel.getCard().getOwner()); + vPlayerPanel = getPlayerPanel(cardView.getOwner()); if (vPlayerPanel != null) { - float cardW = getHeight() * 0.45f; + boolean rotate = CardRendererUtils.needsRotation(cardView) && !Forge.magnifyShowDetails; + boolean inBattlefield = ZoneType.Battlefield.equals(cardView.getZone()); + float mul = 0.45f; + float div = inBattlefield ? cardPanel.isTapped() ? 2.7f : 2.4f : 1.6f; + float adjX = rotate ? cardPanel.getWidth() / div : 0f; + float adjY = rotate ? cardPanel.getHeight() / 2.2f : 0f; + float cardW = getHeight() * mul; float cardH = FCardPanel.ASPECT_RATIO * cardW; - float cardX = !ZoneType.Battlefield.equals(cardPanel.getCard().getZone()) - ? cardPanel.screenPos.x - cardW : cardPanel.screenPos.x + (cardPanel.isTapped() - ? cardPanel.getWidth() : cardPanel.getWidth() / 1.4f); + float cardX = !inBattlefield ? cardPanel.screenPos.x - (cardW + adjX) + : cardPanel.screenPos.x + (cardPanel.isTapped() ? cardPanel.getWidth() + : cardPanel.getWidth() / 1.4f) + adjX; if (vPlayerPanel.getSelectedTab() != null && vPlayerPanel.getSelectedTab().isVisible() && cardX > vPlayerPanel.getSelectedTab().getDisplayArea().getLeft()) { - cardX = cardPanel.screenPos.x - cardW; + cardX = cardPanel.screenPos.x - (cardW + adjX); } - if ((cardX + cardW) > scroller.getWidth() + scroller.getLeft()) - cardX = cardPanel.screenPos.x - cardW; + if ((cardX + cardW + adjX) > scroller.getWidth() + scroller.getLeft()) + cardX = cardPanel.screenPos.x - (cardW + adjX); if (vPlayerPanel.getCommandZone() != null && vPlayerPanel.getCommandZone().isVisible() && cardX > vPlayerPanel.getCommandZone().screenPos.x) - cardX = cardPanel.screenPos.x - cardW; - float cardY = (cardPanel.screenPos.y - cardH) + cardPanel.getHeight(); + cardX = cardPanel.screenPos.x - (cardW + adjX); + float cardY = (cardPanel.screenPos.y - (cardH - adjY)) + cardPanel.getHeight(); if (vPlayerPanel.getPlayer() == bottomPlayerPanel.getPlayer()) { - cardY = bottomPlayerPrompt.screenPos.y - cardH; + cardY = bottomPlayerPrompt.screenPos.y - (cardH - adjY); } else if (cardY < vPlayerPanel.getField().screenPos.y && vPlayerPanel.getPlayer() != bottomPlayerPanel.getPlayer()) { - cardY = vPlayerPanel.getField().screenPos.y; - if ((cardY + cardH) > bottomPlayerPrompt.screenPos.y) - cardY = bottomPlayerPrompt.screenPos.y - cardH; + cardY = vPlayerPanel.getField().screenPos.y - adjY; + if ((cardY + (cardH - adjY)) > bottomPlayerPrompt.screenPos.y) + cardY = bottomPlayerPrompt.screenPos.y - (cardH - adjY); } if (Forge.magnifyShowDetails) - CardImageRenderer.drawDetails(g, cardPanel.getCard(), MatchController.instance.getGameView(), false, cardX, cardY, cardW, cardH); + CardImageRenderer.drawDetails(g, cardView, MatchController.instance.getGameView(), false, cardX, cardY, cardW, cardH); else - CardRenderer.drawCard(g, cardPanel.getCard(), cardX, cardY, cardW, cardH, CardRenderer.CardStackPosition.Top, false, false, false, true); + CardRenderer.drawCard(g, cardView, cardX, cardY, cardW, cardH, CardRenderer.CardStackPosition.Top, rotate, false, false, true); } } } catch (Exception e) { @@ -888,11 +896,11 @@ public class MatchScreen extends FScreen { g.drawRipple(image, x, y, w, h, 1 - percentage); if ("Day".equalsIgnoreCase(dt)) { g.setAlphaComposite(percentage); - g.drawNightDay(image, x, y, w, h, 100f, true, 1 - percentage); + g.drawNightDay(image, x, y, w, h, 100f, true, 0/*1 - percentage*/); // disable extra ripples g.setAlphaComposite(oldAlpha); } else if ("Night".equalsIgnoreCase(dt)) { g.setAlphaComposite(percentage); - g.drawNightDay(image, x, y, w, h, -100f, true, 1 - percentage); + g.drawNightDay(image, x, y, w, h, -100f, true, 0/*1 - percentage*/); // disable extra ripples g.setAlphaComposite(oldAlpha); } } else { diff --git a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java index 4caffab2cba..a96fdd785a6 100644 --- a/forge-gui-mobile/src/forge/toolbox/FChoiceList.java +++ b/forge-gui-mobile/src/forge/toolbox/FChoiceList.java @@ -41,6 +41,7 @@ import forge.localinstance.skin.IHasSkinProp; import forge.screens.match.MatchController; import forge.screens.match.views.VAvatar; import forge.screens.match.views.VStack; +import forge.util.CardRendererUtils; import forge.util.TextUtil; import forge.util.Utils; @@ -440,33 +441,6 @@ public class FChoiceList extends FList implements ActivateHandler { } } - //simple check for cardview needed on some special renderer for cards - private boolean showAlternate(CardView cardView, String value) { - if (cardView == null) - return false; - if (cardView.isFaceDown()) - return false; - boolean showAlt = false; - if (cardView.hasAlternateState()) { - if (cardView.hasBackSide()) - showAlt = value.contains(cardView.getBackSideName()) || cardView.getAlternateState().getAbilityText().contains(value); - else if (cardView.hasSecondaryState()) - showAlt = value.equals(cardView.getAlternateState().getAbilityText()); - else if (cardView.isSplitCard()) { - //special case if aftermath cards can be cast from graveyard like yawgmoths will, you will have choices - if (cardView.getAlternateState().getOracleText().contains("Aftermath")) - showAlt = cardView.getAlternateState().getOracleText().contains(value); - else { - if (cardView.isRoom()) // special case for room cards - showAlt = cardView.getAlternateState().getName().equalsIgnoreCase(value); - else - showAlt = value.equals(cardView.getAlternateState().getAbilityText()); - } - } - } - return showAlt; - } - //special renderer for cards protected class PaperCardItemRenderer extends ItemRenderer { @Override @@ -575,7 +549,7 @@ public class FChoiceList extends FList implements ActivateHandler { } } CardView cv = ((IHasCardView) value).getCardView(); - CardZoom.show(cv, showAlternate(cv, value.toString())); + CardZoom.show(cv, CardRendererUtils.canShowAlternate(cv, value.toString())); } catch (Exception ignored) { //fixme: java.lang.ClassCastException for cards like Subtlety which should be cancelable instead... } @@ -595,7 +569,7 @@ public class FChoiceList extends FList implements ActivateHandler { } } CardView cv = ((IHasCardView) value).getCardView(); - CardZoom.show(cv, showAlternate(cv, value.toString())); + CardZoom.show(cv, CardRendererUtils.canShowAlternate(cv, value.toString())); } catch (Exception ignored) { //fixme: java.lang.ClassCastException for cards like Subtlety which should be cancelable instead... } @@ -612,7 +586,7 @@ public class FChoiceList extends FList implements ActivateHandler { if (morph != null) { g.drawImage(morph, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT); } else if (cv != null) { - boolean showAlternate = showAlternate(cv, value.toString()); + boolean showAlternate = CardRendererUtils.canShowAlternate(cv, value.toString()); if (!cv.isFaceDown()) CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true); else @@ -620,7 +594,7 @@ public class FChoiceList extends FList implements ActivateHandler { } } else { if (cv != null) { - boolean showAlternate = showAlternate(cv, value.toString()); + boolean showAlternate = CardRendererUtils.canShowAlternate(cv, value.toString()); if (!cv.isFaceDown()) CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true); else diff --git a/forge-gui-mobile/src/forge/util/CardRendererUtils.java b/forge-gui-mobile/src/forge/util/CardRendererUtils.java new file mode 100644 index 00000000000..0a5c139fcae --- /dev/null +++ b/forge-gui-mobile/src/forge/util/CardRendererUtils.java @@ -0,0 +1,119 @@ +package forge.util; + +import forge.Forge; +import forge.game.card.CardView; +import forge.localinstance.properties.ForgePreferences; +import forge.model.FModel; +import forge.screens.match.MatchController; + +public class CardRendererUtils { + public static boolean needsRotation(final CardView card) { + return needsRotation(card.isSplitCard() ? ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS + : ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON, card, canShowAlternate(card, card.getName())); + } + public static boolean needsRotation(final CardView card, final boolean altState) { + return needsRotation(card.isSplitCard() ? ForgePreferences.FPref.UI_ROTATE_SPLIT_CARDS + : ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON, card, altState); + } + public static boolean needsRotation(final ForgePreferences.FPref fPref, final CardView card, boolean altState) { + if (isPreferenceEnabled(fPref)) { + if (Forge.enableUIMask.equals("Art")) + return false; + switch (fPref) { + case UI_ROTATE_SPLIT_CARDS -> { + return card.isSplitCard() && MatchController.instance.mayView(card) && !card.isFaceDown(); + } + case UI_ROTATE_PLANE_OR_PHENOMENON -> { + return card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() + || (card.getCurrentState().isBattle() && !altState) + || (card.getAlternateState() != null && card.getAlternateState().isBattle() && altState); + } + default -> { + return false; + } + } + } + return false; + } + public static boolean canShowAlternate(final CardView card, final String reference) { + if (card == null) + return false; + if (card.isFaceDown()) + return false; + boolean showAlt = false; + if (card.hasAlternateState()) { + if (card.hasBackSide()) + showAlt = reference.contains(card.getBackSideName()) || card.getAlternateState().getAbilityText().contains(reference); + else if (card.hasSecondaryState()) + showAlt = reference.equals(card.getAlternateState().getAbilityText()); + else if (card.isSplitCard()) { + //special case if aftermath cards can be cast from graveyard like yawgmoths will, you will have choices + if (card.getAlternateState().getOracleText().contains("Aftermath")) + showAlt = card.getAlternateState().getOracleText().contains(reference); + else { + if (card.isRoom()) // special case for room cards + showAlt = card.getAlternateState().getName().equalsIgnoreCase(reference); + else + showAlt = reference.equals(card.getAlternateState().getAbilityText()); + } + } + } + return showAlt; + } + public static boolean hasAftermath(final CardView card) { + if (card.hasAlternateState()) + return card.getAlternateState().hasAftermath(); + return false; + } + + + public static boolean isPreferenceEnabled(final ForgePreferences.FPref preferenceName) { + return FModel.getPreferences().getPrefBoolean(preferenceName); + } + + public static boolean isShowingOverlays(final CardView card) { + return isPreferenceEnabled(ForgePreferences.FPref.UI_SHOW_CARD_OVERLAYS) && card != null; + } + + public static boolean showCardNameOverlay(final CardView card) { + return isShowingOverlays(card) && isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_CARD_NAME); + } + + public static boolean showCardPowerOverlay(final CardView card) { + return isShowingOverlays(card) && isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_CARD_POWER); + } + + public static boolean showCardManaCostOverlay(final CardView card) { + return isShowingOverlays(card) && + isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_CARD_MANA_COST); + } + + public static boolean showAbilityIcons(final CardView card) { + return isShowingOverlays(card) && isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_ABILITY_ICONS); + } + + public static boolean showCardIdOverlay(final CardView card) { + return card.getId() > 0 && isShowingOverlays(card) && isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_CARD_ID); + } + + public static boolean drawGray(final CardView card) { + if (card == null) + return false; + return card.wasDestroyed() || card.isPhasedOut(); + } + public static boolean drawFoil(final CardView card) { + if (card == null) + return false; + if (isPreferenceEnabled(ForgePreferences.FPref.UI_OVERLAY_FOIL_EFFECT)) + return card.hasPaperFoil(); // TODO the Card BG should be the texture instead of the Foil Overlay + return false; + } + public static boolean drawCracks(final CardView card, final boolean isMagnify) { + if (card == null) + return false; + if (isMagnify) + return false; + return card.getDamage() > 0; + } + +} diff --git a/forge-gui/res/skins/default/holofoil.png b/forge-gui/res/skins/default/holofoil.png new file mode 100644 index 0000000000000000000000000000000000000000..af338fadf9c4758700a596e5275d61fc2140dd40 GIT binary patch literal 53288 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 iFh36R9Vq?0^Zq~VhSwYJUw;+=0000