From 54b252c33c94aed88f6b9c9d815dee8eaa2c2559 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 18 Sep 2021 13:40:13 +0800 Subject: [PATCH] [Mobile] Add Card Tap/Untap & Match Background Animation --- forge-gui-mobile/src/forge/Forge.java | 2 + .../src/forge/screens/match/MatchScreen.java | 49 ++++++- .../forge/screens/settings/SettingsPage.java | 10 ++ .../src/forge/toolbox/FCardPanel.java | 122 +++++++++++++++++- forge-gui/res/languages/de-DE.properties | 2 + forge-gui/res/languages/en-US.properties | 2 + forge-gui/res/languages/es-ES.properties | 2 + forge-gui/res/languages/it-IT.properties | 2 + forge-gui/res/languages/ja-JP.properties | 2 + forge-gui/res/languages/zh-CN.properties | 2 + .../gamemodes/match/AbstractGuiGame.java | 3 + .../match/input/InputPlaybackControl.java | 1 + .../java/forge/gui/interfaces/IGuiGame.java | 1 + .../properties/ForgePreferences.java | 1 + 14 files changed, 190 insertions(+), 11 deletions(-) diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index ffcb2055a62..cb2ccbe62c1 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -70,6 +70,7 @@ public class Forge implements ApplicationListener { public static boolean allowCardBG = false; public static boolean altPlayerLayout = false; public static boolean altZoneTabs = false; + public static boolean animatedCardTapUntap = false; public static String enableUIMask = "Crop"; public static boolean enablePreloadExtendedArt = false; public static boolean isTabletDevice = false; @@ -146,6 +147,7 @@ public class Forge implements ApplicationListener { showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS); altPlayerLayout = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT); altZoneTabs = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERZONETABS); + animatedCardTapUntap = prefs.getPrefBoolean(FPref.UI_ANIMATED_CARD_TAPUNTAP); enableUIMask = prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING); if (prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING).equals("true")) //override old settings if not updated enableUIMask = "Full"; diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index cb3ff5f9b66..70f003fce34 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -7,6 +7,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import forge.animation.ForgeAnimation; +import forge.assets.FImage; import org.apache.commons.lang3.tuple.Pair; import com.badlogic.gdx.Input.Keys; @@ -76,7 +78,7 @@ public class MatchScreen extends FScreen { private final VPrompt bottomPlayerPrompt, topPlayerPrompt; private VPlayerPanel bottomPlayerPanel, topPlayerPanel; private AbilityEffect activeEffect; - + private BGAnimation bgAnimation; private ViewWinLose viewWinLose = null; public MatchScreen(List playerPanels0) { @@ -577,14 +579,45 @@ public class MatchScreen extends FScreen { } } + private class BGAnimation extends ForgeAnimation { + private static final float DURATION = 0.2f; + private float progress = 0; + private boolean finished; + + private void drawBackground(Graphics g, FImage image, float x, float y, float w, float h, boolean darkoverlay) { + float percentage = progress / DURATION; + float oldAlpha = g.getfloatAlphaComposite(); + if (percentage < 0) { + percentage = 0; + } else if (percentage > 1) { + percentage = 1; + } + g.setAlphaComposite(percentage); + g.drawImage(image, x, y, w, h, darkoverlay); + g.setAlphaComposite(oldAlpha); + } + + @Override + protected boolean advance(float dt) { + progress += dt; + return progress < DURATION; + } + + @Override + protected void onEnd(boolean endingAll) { + finished = true; + } + } private class FieldScroller extends FScrollPane { private float extraHeight = 0; + private String plane = ""; @Override public void drawBackground(Graphics g) { super.drawBackground(g); if (FModel.getPreferences().getPrefBoolean(FPref.UI_MATCH_IMAGE_VISIBLE)) { + boolean isGameFast = MatchController.instance.isGameFast(); float midField = topPlayerPanel.getBottom(); float x = topPlayerPanel.getField().getLeft(); float y = midField - topPlayerPanel.getField().getHeight(); @@ -598,6 +631,11 @@ public class MatchScreen extends FScreen { .replace(" ", "_") .replace("'", "") .replace("-", ""); + if (!plane.equals(imageName)) { + bgAnimation = new BGAnimation(); + bgAnimation.start(); + plane = imageName; + } if (FSkinTexture.getValues().contains(imageName)) { bgFullWidth = bgHeight * FSkinTexture.valueOf(imageName).getWidth() / FSkinTexture.valueOf(imageName).getHeight(); if (bgFullWidth < w) { @@ -605,10 +643,13 @@ public class MatchScreen extends FScreen { bgFullWidth = w; bgHeight = scaledbgHeight; } - g.drawImage(FSkinTexture.valueOf(imageName), x + (w - bgFullWidth) / 2, y, bgFullWidth, bgHeight, true); + if (bgAnimation != null && !isGameFast) { + bgAnimation.drawBackground(g, FSkinTexture.valueOf(imageName), x + (w - bgFullWidth) / 2, y, bgFullWidth, bgHeight, true); + } else { + g.drawImage(FSkinTexture.valueOf(imageName), x + (w - bgFullWidth) / 2, y, bgFullWidth, bgHeight, true); + } } - } - else { + } else { bgFullWidth = bgHeight * FSkinTexture.BG_MATCH.getWidth() / FSkinTexture.BG_MATCH.getHeight(); if (bgFullWidth < w) { scaledbgHeight = w * (bgHeight / bgFullWidth); diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index 8bc21cf3f66..b5545b49152 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -259,6 +259,16 @@ public class SettingsPage extends TabPage { MatchController.instance.resetPlayerPanels(); } },1); + lstSettings.addItem(new BooleanSetting(FPref.UI_ANIMATED_CARD_TAPUNTAP, + localizer.getMessage("lblAnimatedCardTapUntap"), + localizer.getMessage("nlAnimatedCardTapUntap")){ + @Override + public void select() { + super.select(); + //update + Forge.animatedCardTapUntap = FModel.getPreferences().getPrefBoolean(FPref.UI_ANIMATED_CARD_TAPUNTAP); + } + },1); //Random Deck Generation lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_NOSMALL, diff --git a/forge-gui-mobile/src/forge/toolbox/FCardPanel.java b/forge-gui-mobile/src/forge/toolbox/FCardPanel.java index 3e75ee0464f..8819e80ebf7 100644 --- a/forge-gui-mobile/src/forge/toolbox/FCardPanel.java +++ b/forge-gui-mobile/src/forge/toolbox/FCardPanel.java @@ -1,9 +1,13 @@ package forge.toolbox; +import forge.Forge; import forge.Graphics; +import forge.animation.ForgeAnimation; import forge.card.CardRenderer; import forge.card.CardRenderer.CardStackPosition; import forge.game.card.CardView; +import forge.game.zone.ZoneType; +import forge.screens.match.MatchController; import forge.util.Utils; public class FCardPanel extends FDisplayObject { @@ -15,12 +19,17 @@ public class FCardPanel extends FDisplayObject { private CardView card; private boolean tapped; private boolean highlighted; + private boolean wasTapped; + CardTapAnimation tapAnimation; + CardUnTapAnimation untapAnimation; public FCardPanel() { this(null); } public FCardPanel(CardView card0) { card = card0; + tapAnimation = new CardTapAnimation(); + untapAnimation = new CardUnTapAnimation(); } public CardView getCard() { @@ -76,10 +85,75 @@ public class FCardPanel extends FDisplayObject { return CardStackPosition.Top; } + private class CardUnTapAnimation extends ForgeAnimation { + private static final float DURATION = 0.14f; + private float progress = 0; + private boolean finished; + + private void drawCard(Graphics g, CardView card, float x, float y, float w, float h, float edgeOffset) { + float percentage = progress / DURATION; + if (percentage < 0) { + percentage = 0; + } else if (percentage > 1) { + percentage = 1; + } + float angle = -90 + (percentage*90); + if (wasTapped) { + g.startRotateTransform(x + edgeOffset, y + h - edgeOffset, angle); + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + g.endTransform(); + } else { + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + } + } + + @Override + protected boolean advance(float dt) { + progress += dt; + return progress < DURATION; + } + + @Override + protected void onEnd(boolean endingAll) { + finished = true; + } + } + + private class CardTapAnimation extends ForgeAnimation { + private static final float DURATION = 0.18f; + private float progress = 0; + private boolean finished; + + private void drawCard(Graphics g, CardView card, float x, float y, float w, float h, float edgeOffset, float angle) { + float percentage = progress / DURATION; + if (percentage > 1) { + percentage = 1; + wasTapped = true; + } + g.startRotateTransform(x + edgeOffset, y + h - edgeOffset, percentage*angle); + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + g.endTransform(); + } + + @Override + protected boolean advance(float dt) { + progress += dt; + return progress < DURATION; + } + + @Override + protected void onEnd(boolean endingAll) { + finished = true; + } + } + @Override public void draw(Graphics g) { if (card == null) { return; } + boolean isGameFast = MatchController.instance.isGameFast(); + boolean animate = ZoneType.Battlefield.equals(card.getZone()) && Forge.animatedCardTapUntap; + float padding = getPadding(); float x = padding; float y = padding; @@ -88,16 +162,50 @@ public class FCardPanel extends FDisplayObject { if (w == h) { //adjust width if needed to make room for tapping w = h / ASPECT_RATIO; } + float edgeOffset = w / 2f; + if (isGameFast) { + //don't animate if fast + if (tapped) { + g.startRotateTransform(x + edgeOffset, y + h - edgeOffset, getTappedAngle()); + } - if (tapped) { - float edgeOffset = w / 2f; - g.startRotateTransform(x + edgeOffset, y + h - edgeOffset, getTappedAngle()); - } + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); - CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + if (tapped) { + g.endTransform(); + } - if (tapped) { - g.endTransform(); + } else { + if (tapped) { + //reset untapAnimation + if (untapAnimation != null) { + untapAnimation.progress = 0; + } + //draw tapped + if (tapAnimation != null) { + if (tapAnimation.progress < 1 && animate) { + tapAnimation.start(); + tapAnimation.drawCard(g, card, x, y, w, h, w / 2f, getTappedAngle()); + } else { + g.startRotateTransform(x + edgeOffset, y + h - edgeOffset, getTappedAngle()); + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + g.endTransform(); + } + } + } else { + //reset tapAnimation + if (tapAnimation != null) { + tapAnimation.progress = 0; + } + //draw untapped + if (untapAnimation.progress < 1 && animate) { + untapAnimation.start(); + untapAnimation.drawCard(g, card, x, y, w, h, edgeOffset); + } else { + wasTapped = false; + CardRenderer.drawCardWithOverlays(g, card, x, y, w, h, getStackPosition()); + } + } } } diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index 5bd3ad388e9..812a261ec8d 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -121,6 +121,8 @@ lblAltLifeDisplay=Alternatives Spieler-Layout (Landscape-Modus) nlAltLifeDisplay=Alternative Anzeige von Lebens-, Gift-, Energie- und Erfahrungspunkten. lblAltZoneTabs=Alternatives Spielerbereich-Layout (Landscape-Modus) nlAltZoneTabs=Alternative Anzeige von Kartenhand, Friedhof, Bibliothek und Exilzone. +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=Kartenbild-Präferenz nlPreferredArt=Gibt an, welche Grafik für Karten ausgewählt werden soll, wenn keine Edition angegeben ist. lblPrefArtExpansionOnly=Kartenbilder aus Haupt-, Erweiterungs- und Reprint-Sets bevorzugen diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 7a138580850..acb8b55aeb4 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -122,6 +122,8 @@ lblAltLifeDisplay=Alternate Player Layout (Landscape Mode) nlAltLifeDisplay=Enables alternate layout for displaying Player Life, Poison, Energy and Experience counters. lblAltZoneTabs=Alternate Player Zone Layout (Landscape Mode) nlAltZoneTabs=Enables alternate layout for displaying Player Hand, Graveyard, Library and Exile zones. +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=Card Art Preference nlPreferredArt=Specifies which art should be selected for cards when no Edition is specified. lblPrefArtExpansionOnly=Prefer Card Art from Core, Expansions, and Reprint Sets diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index bf49a017f08..ef943ac80e2 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -122,6 +122,8 @@ lblAltLifeDisplay=Diseño alternativo de jugador (Modo apaisado) nlAltLifeDisplay=Permite un diseño alternativo para mostrar los contadores de vida, veneno, energía y experiencia. lblAltZoneTabs=Diseño alternativo de la zona del jugador (Modo apaisado) nlAltZoneTabs=Permite un diseño alternativo para mostrar las zonas de la mano del jugador, el cementerio, la biblioteca y el exilio. +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=Preferencia de arte de carta nlPreferredArt=Especifica qué arte debe seleccionarse para las cartas cuando no se especifica ninguna edición. lblPrefArtExpansionOnly=Prefiere el arte de la carta de los conjuntos básicos, de expansión y de reimpresión diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index a798eb0e077..2af8a634cce 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -121,6 +121,8 @@ lblAltLifeDisplay=Layout alternativo del giocatore (modalità panorama) nlAltLifeDisplay=Abilita un layout alternativo per visalizzare i punti vita e i segnalini veleno, energia ed esperienza di un giocatore. lblAltZoneTabs=Layout alternativo delle zone di gioco (modalità panorama) nlAltZoneTabs=Abilita un layout alternativo per visalizzare la mano del giocatore, il cimitero, il grimorio e la zona di esilio. +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=Preferenza per Illustrazione delle Carte nlPreferredArt=Specifica quale illustrazione viene scelta per le carte, quando non è specificata alcuna espansione. lblPrefArtExpansionOnly=Limita la selezione dell'illustrazione delle carte da Set Base, Espansioni, e Set Ristampa diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index f4f79519e18..bb83712589c 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -122,6 +122,8 @@ lblAltLifeDisplay=代替のプレイヤーライフレイアウト(ランド nlAltLifeDisplay=代替のレイアウトでプレイヤーライフ、毒、エネルギーと経験カウンターを表示する lblAltZoneTabs=代替のプレイヤーゾーンレイアウト(ランドスケープモード時) nlAltZoneTabs=代替のレイアウトで手札、墓地、ライブラリーと除外領域を表示する +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=カードアートの好み nlPreferredArt=エディションが指定されていない場合にカードに選択するアートを指定します。 lblPrefArtExpansionOnly=コア、拡張、および再版セットからカードアートを好む diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 5c91cf0566b..2dab37db94e 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -122,6 +122,8 @@ lblAltLifeDisplay=备用牌手布局(横向模式) nlAltLifeDisplay=启用备用牌手布局以显示玩家的生命以及中毒,能量和经验指示物。 lblAltZoneTabs=备用牌手区域布局(用于横向模式) nlAltZoneTabs=启用用于显示牌手手牌、坟场、牌库、放逐区的备用布局。 +lblAnimatedCardTapUntap=Enable Card Tap & Untap Animation +nlAnimatedCardTapUntap=Enables Card Tap & Untap Animation during gameplay. lblPreferredArt=卡片艺术偏好 nlPreferredArt=指定在未指定版本时应为卡片选择哪种艺术。 lblPrefArtExpansionOnly=首选核心、扩展和重印套装中的卡片艺术 diff --git a/forge-gui/src/main/java/forge/gamemodes/match/AbstractGuiGame.java b/forge-gui/src/main/java/forge/gamemodes/match/AbstractGuiGame.java index 674158ad0c2..ad8514316d0 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/AbstractGuiGame.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/AbstractGuiGame.java @@ -43,6 +43,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards { private final Map gameControllers = Maps.newHashMap(); private final Map originalGameControllers = Maps.newHashMap(); private boolean gamePause = false; + private boolean gameSpeed = false; private boolean ignoreConcedeChain = false; public final boolean hasLocalPlayers() { @@ -281,7 +282,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards { return !selectableCards.isEmpty(); } public boolean isGamePaused() { return gamePause; } + public boolean isGameFast() { return gameSpeed; } public void setgamePause(boolean pause) { gamePause = pause; } + public void setGameSpeed(boolean isFast) { gameSpeed = isFast; } public void pauseMatch() { IGameController controller = spectator; if (controller != null && !isGamePaused()) diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPlaybackControl.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPlaybackControl.java index 06ed7c70539..5217ded0a16 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPlaybackControl.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPlaybackControl.java @@ -77,6 +77,7 @@ public class InputPlaybackControl extends InputSyncronizedBase { else { isFast = !isFast; control.setSpeed(isFast); + getController().getGui().setGameSpeed(isFast); setPause(isPaused); // update message } } diff --git a/forge-gui/src/main/java/forge/gui/interfaces/IGuiGame.java b/forge-gui/src/main/java/forge/gui/interfaces/IGuiGame.java index 0594d3ee8e3..b036c422a60 100644 --- a/forge-gui/src/main/java/forge/gui/interfaces/IGuiGame.java +++ b/forge-gui/src/main/java/forge/gui/interfaces/IGuiGame.java @@ -174,6 +174,7 @@ public interface IGuiGame { boolean isSelecting(); boolean isGamePaused(); public void setgamePause(boolean pause); + public void setGameSpeed(boolean gameSpeed); void awaitNextInput(); void cancelAwaitNextInput(); diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java index bbf8123b0f1..10d376a6b25 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java @@ -146,6 +146,7 @@ public class ForgePreferences extends PreferencesStore { UI_ANDROID_MINIMIZE_ON_SCRLOCK("false"), UI_ROTATE_PLANE_OR_PHENOMENON("false"), UI_ROTATE_SPLIT_CARDS("true"), + UI_ANIMATED_CARD_TAPUNTAP("true"), UI_DYNAMIC_PLANECHASE_BG("false"), UI_DISABLE_IMAGES_EFFECT_CARDS("false"), UI_ENABLE_PRELOAD_EXTENDED_ART("false"),