From 53b2d87e78915e919d22accbbbc4d0b6cbf6601a Mon Sep 17 00:00:00 2001 From: drdev Date: Thu, 26 Jun 2014 04:06:10 +0000 Subject: [PATCH] Refactor and cleanup targeting arrow code --- .../forge/screens/match/TargetingOverlay.java | 209 ++++++++++-------- .../forge/screens/match/views/VAvatar.java | 15 ++ .../screens/match/views/VCardDisplayArea.java | 4 + .../src/forge/screens/match/views/VStack.java | 8 +- .../src/forge/toolbox/FCardPanel.java | 6 +- 5 files changed, 151 insertions(+), 91 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java b/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java index 5a126e75c87..0b443f9717c 100644 --- a/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java +++ b/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java @@ -22,129 +22,164 @@ import forge.assets.FSkinColor; import forge.assets.FSkinColor.Colors; import forge.game.card.Card; import forge.game.combat.Combat; +import forge.game.player.Player; +import forge.game.spellability.SpellAbilityStackInstance; +import forge.game.spellability.TargetChoices; import forge.screens.match.views.VCardDisplayArea.CardAreaPanel; import forge.screens.match.views.VPlayerPanel; +import forge.screens.match.views.VStack; +import forge.screens.match.views.VStack.StackInstanceDisplay; import forge.toolbox.FCardPanel; +import forge.toolbox.FDisplayObject; import forge.util.Utils; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; import java.util.ArrayList; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; public class TargetingOverlay { private static final float ARROW_THICKNESS = Utils.scaleMax(5); private static final float ARROW_SIZE = 3 * ARROW_THICKNESS; - private final List cardPanels = new ArrayList(); - private final List arcsCombat = new ArrayList(); - private final List arcsOther = new ArrayList(); + private final List combatArrows = new ArrayList(); + private final List targetArrows = new ArrayList(); + private final List pairArrows = new ArrayList(); public TargetingOverlay() { } - // TODO - this is called every repaint, regardless if card - // positions have changed or not. Could perform better if - // it checked for a state change. - private void assembleArcs() { - //List fields = VMatchUI.SINGLETON_INSTANCE.getFieldViews(); - arcsCombat.clear(); - arcsOther.clear(); - cardPanels.clear(); - - for (VPlayerPanel pnl : FControl.getView().getPlayerPanels().values()) { - for (CardAreaPanel cardPanel : pnl.getField().getCardPanels()) { - cardPanel.buildCardPanelList(cardPanels); - } - } - - // Locations of arc endpoint, per card, with ID as primary key. - final Map endpoints = new HashMap(); - - for (FCardPanel c : cardPanels) { - endpoints.put(c.getCard().getUniqueNumber(), c.getTargetingArrowOrigin()); - } + private void refreshCombatArrows() { + combatArrows.clear(); final Combat combat = FControl.getGame().getCombat(); - - // Work with all card panels currently visible - List visualized = new ArrayList(); - for (FCardPanel c : cardPanels) { - Card card = c.getCard(); - if (visualized.contains(card)) { continue; } - - visualized.addAll(addArcsForCard(card, endpoints, combat)); - } - } - - private List addArcsForCard(final Card c, final Map endpoints, final Combat combat) { - List cardsVisualized = new ArrayList(); - cardsVisualized.add(c); - - Card paired = c.getPairedWith(); - if (paired != null) { - arcsOther.add(new Vector2[] { - endpoints.get(paired.getUniqueNumber()), - endpoints.get(c.getUniqueNumber()) - }); - cardsVisualized.add(paired); - } - if (combat != null) { + //connect each attacker with planeswalker it's attacking if applicable for (Card planeswalker : combat.getDefendingPlaneswalkers()) { - List cards = combat.getAttackersOf(planeswalker); - for (Card pwAttacker : cards) { - if (!planeswalker.equals(c) && !pwAttacker.equals(c)) { continue; } - arcsCombat.add(new Vector2[] { - endpoints.get(planeswalker.getUniqueNumber()), - endpoints.get(pwAttacker.getUniqueNumber()) - }); + for (Card attacker : combat.getAttackersOf(planeswalker)) { + combatArrows.add(new Arrow(attacker, planeswalker)); } } - for (Card attackingCard : combat.getAttackers()) { - List cards = combat.getBlockers(attackingCard); - for (Card blockingCard : cards) { - if (!attackingCard.equals(c) && !blockingCard.equals(c)) { continue; } - arcsCombat.add(new Vector2[] { - endpoints.get(attackingCard.getUniqueNumber()), - endpoints.get(blockingCard.getUniqueNumber()) - }); - cardsVisualized.add(blockingCard); + + //connect each blocker with the attacker it's blocking + for (Card blocker : combat.getAllBlockers()) { + for (Card attacker : combat.getAttackersBlockedBy(blocker)) { + combatArrows.add(new Arrow(blocker, attacker)); } - cardsVisualized.add(attackingCard); } } - - return cardsVisualized; } - public void drawArcs(Graphics g, FSkinColor color, List arcs) { - for (Vector2[] p : arcs) { - if (p[0] != null && p[1] != null) { - g.drawArrow(ARROW_THICKNESS, ARROW_SIZE, color, p[1].x, p[1].y, p[0].x, p[0].y); + private void refreshTargetArrows() { + targetArrows.clear(); + + VStack stack = FControl.getView().getStack(); + if (stack.isVisible()) { + for (FDisplayObject child : stack.getChildren()) { + Vector2 arrowOrigin = new Vector2( + VStack.CARD_WIDTH * FCardPanel.TARGET_ORIGIN_FACTOR_X + 2 * VStack.PADDING, + VStack.CARD_HEIGHT * FCardPanel.TARGET_ORIGIN_FACTOR_Y + 2 * VStack.PADDING); + + float y = child.getTop() + arrowOrigin.y; + if (y < 0) { + continue; //don't draw arrow scrolled off top + } + if (y > child.getHeight()) { + break; //don't draw arrow scrolled off bottom + } + + SpellAbilityStackInstance stackInstance = ((StackInstanceDisplay)child).getStackInstance(); + TargetChoices targets = stackInstance.getSpellAbility().getTargets(); + arrowOrigin = arrowOrigin.add(stack.getScreenPosition()); + + for (Card c : targets.getTargetCards()) { + targetArrows.add(new Arrow(arrowOrigin, c)); + } + for (Player p : targets.getTargetPlayers()) { + targetArrows.add(new Arrow(arrowOrigin, p)); + } + } + } + } + + private void refreshPairArrows() { + pairArrows.clear(); + + HashSet pairedCards = new HashSet(); + for (VPlayerPanel playerPanel : FControl.getView().getPlayerPanels().values()) { + for (Card card : playerPanel.getField().getRow1().getOrderedCards()) { + if (pairedCards.contains(card)) { continue; } //prevent arrows going both ways + + Card paired = card.getPairedWith(); + if (paired != null) { + pairArrows.add(new Arrow(card, paired)); + pairedCards.add(paired); + } } } } public void draw(final Graphics g) { - assembleArcs(); - - if (arcsCombat.isEmpty() && arcsOther.isEmpty()) { return; } - - // Get arrow colors from the theme or use default colors if the theme does not have them defined - FSkinColor colorOther = FSkinColor.get(Colors.CLR_NORMAL_TARGETING_ARROW); - if (colorOther.getAlpha() == 0) { - colorOther = FSkinColor.get(Colors.CLR_ACTIVE).alphaColor(153f / 255f); - } - FSkinColor colorCombat = FSkinColor.get(Colors.CLR_COMBAT_TARGETING_ARROW); - if (colorCombat.getAlpha() == 0) { - colorCombat = FSkinColor.getStandardColor(new Color(1, 0, 0, 153 / 255f)); + refreshPairArrows(); //TODO: Optimize so these don't need to be refreshed every render + if (!pairArrows.isEmpty()) { + FSkinColor color = FSkinColor.get(Colors.CLR_NORMAL_TARGETING_ARROW); + if (color.getAlpha() == 0) { + color = FSkinColor.get(Colors.CLR_ACTIVE).alphaColor(153f / 255f); + } + for (Arrow arrow : pairArrows) { + arrow.draw(g, color); + } } - drawArcs(g, colorOther, arcsOther); - drawArcs(g, colorCombat, arcsCombat); + refreshCombatArrows(); + if (!combatArrows.isEmpty()) { + FSkinColor color = FSkinColor.get(Colors.CLR_COMBAT_TARGETING_ARROW); + if (color.getAlpha() == 0) { + color = FSkinColor.getStandardColor(new Color(1, 0, 0, 153 / 255f)); + } + for (Arrow arrow : combatArrows) { + arrow.draw(g, color); + } + } + + refreshTargetArrows(); + if (!targetArrows.isEmpty()) { + FSkinColor color = FSkinColor.get(Colors.CLR_COMBAT_TARGETING_ARROW); //TODO: Consider a different color for targeting + if (color.getAlpha() == 0) { + color = FSkinColor.getStandardColor(new Color(1, 0, 0, 153 / 255f)); + } + for (Arrow arrow : targetArrows) { + arrow.draw(g, color); + } + } + } + + private static class Arrow { + private final Vector2 start, end; + + private Arrow(Card startCard, Card endCard) { + this(CardAreaPanel.get(startCard).getTargetingArrowOrigin(), CardAreaPanel.get(endCard).getTargetingArrowOrigin()); + } + private Arrow(Card card, Player player) { + this(CardAreaPanel.get(card).getTargetingArrowOrigin(), FControl.getPlayerPanel(player).getAvatar().getTargetingArrowOrigin()); + } + private Arrow(Vector2 start0, Card targetCard) { + this(start0, CardAreaPanel.get(targetCard).getTargetingArrowOrigin()); + } + private Arrow(Vector2 start0, Player targetPlayer) { + this(start0, FControl.getPlayerPanel(targetPlayer).getAvatar().getTargetingArrowOrigin()); + } + private Arrow(Vector2 start0, Vector2 end0) { + start = start0; + end = end0; + } + + private void draw(Graphics g, FSkinColor color) { + if (start == null || end == null) { return; } + + g.drawArrow(ARROW_THICKNESS, ARROW_SIZE, color, start.x, start.y, end.x, end.y); + } } } diff --git a/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java b/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java index fa0058fbe7d..06638ac379d 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java @@ -1,6 +1,7 @@ package forge.screens.match.views; import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.math.Vector2; import forge.Graphics; import forge.assets.FSkin; @@ -34,6 +35,20 @@ public class VAvatar extends FDisplayObject { return true; } + public Vector2 getTargetingArrowOrigin() { + Vector2 origin = new Vector2(getScreenPosition()); + + origin.x += WIDTH * 0.75f; + if (origin.y < FControl.getView().getHeight() / 2) { + origin.y += HEIGHT * 0.75f; //target bottom right corner if on top half of screen + } + else { + origin.y += HEIGHT * 0.25f; //target top right corner if on bottom half of screen + } + + return origin; + } + @Override public void draw(Graphics g) { float w = getWidth(); diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java index 7cfa7380241..d2d5dd09b61 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java @@ -19,6 +19,10 @@ public abstract class VCardDisplayArea extends VDisplayArea { protected final List orderedCards = new ArrayList(); protected final List cardPanels = new ArrayList(); + public Iterable getOrderedCards() { + return orderedCards; + } + public Iterable getCardPanels() { return cardPanels; } diff --git a/forge-gui-mobile/src/forge/screens/match/views/VStack.java b/forge-gui-mobile/src/forge/screens/match/views/VStack.java index 328257d4511..71f9359e686 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VStack.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VStack.java @@ -31,7 +31,7 @@ import forge.util.Utils; public class VStack extends FDropDown { public static final float CARD_WIDTH = Utils.AVG_FINGER_WIDTH; public static final float CARD_HEIGHT = Math.round(CARD_WIDTH * FCardPanel.ASPECT_RATIO); - private static final float PADDING = Utils.scaleMin(3); + public static final float PADDING = Utils.scaleMin(3); private static final FSkinFont FONT = FSkinFont.get(11); private static final float ALPHA_COMPOSITE = 0.5f; private static final TextRenderer textRenderer = new TextRenderer(true); @@ -111,7 +111,7 @@ public class VStack extends FDropDown { super.setScrollPositionsAfterLayout(0, 0); //always scroll to top after layout } - private class StackInstanceDisplay extends FDisplayObject { + public class StackInstanceDisplay extends FDisplayObject { private final SpellAbilityStackInstance stackInstance; private final boolean isTop; private final Color foreColor, backColor; @@ -141,6 +141,10 @@ public class VStack extends FDropDown { return Math.round(height); } + public SpellAbilityStackInstance getStackInstance() { + return stackInstance; + } + @Override public boolean tap(float x, float y, int count) { final Player player = stackInstance.getSpellAbility().getActivatingPlayer(); diff --git a/forge-gui-mobile/src/forge/toolbox/FCardPanel.java b/forge-gui-mobile/src/forge/toolbox/FCardPanel.java index 7653554d102..8916359bb94 100644 --- a/forge-gui-mobile/src/forge/toolbox/FCardPanel.java +++ b/forge-gui-mobile/src/forge/toolbox/FCardPanel.java @@ -11,6 +11,8 @@ public class FCardPanel extends FDisplayObject { public static final float TAPPED_ANGLE = -90; public static final float ASPECT_RATIO = 3.5f / 2.5f; public static final float PADDING = Utils.scaleMin(2); + public static final float TARGET_ORIGIN_FACTOR_X = 0.15f; + public static final float TARGET_ORIGIN_FACTOR_Y = 0.5f; private Card card; private boolean tapped; @@ -93,8 +95,8 @@ public class FCardPanel extends FDisplayObject { h = temp; } - origin.x += left + w * 0.15f; - origin.y += top + h * 0.5f; + origin.x += left + w * TARGET_ORIGIN_FACTOR_X; + origin.y += top + h * TARGET_ORIGIN_FACTOR_Y; return origin; }