mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Refactor and cleanup targeting arrow code
This commit is contained in:
@@ -22,129 +22,164 @@ import forge.assets.FSkinColor;
|
|||||||
import forge.assets.FSkinColor.Colors;
|
import forge.assets.FSkinColor.Colors;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.combat.Combat;
|
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.VCardDisplayArea.CardAreaPanel;
|
||||||
import forge.screens.match.views.VPlayerPanel;
|
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.FCardPanel;
|
||||||
|
import forge.toolbox.FDisplayObject;
|
||||||
import forge.util.Utils;
|
import forge.util.Utils;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class TargetingOverlay {
|
public class TargetingOverlay {
|
||||||
private static final float ARROW_THICKNESS = Utils.scaleMax(5);
|
private static final float ARROW_THICKNESS = Utils.scaleMax(5);
|
||||||
private static final float ARROW_SIZE = 3 * ARROW_THICKNESS;
|
private static final float ARROW_SIZE = 3 * ARROW_THICKNESS;
|
||||||
|
|
||||||
private final List<FCardPanel> cardPanels = new ArrayList<FCardPanel>();
|
private final List<Arrow> combatArrows = new ArrayList<Arrow>();
|
||||||
private final List<Vector2[]> arcsCombat = new ArrayList<Vector2[]>();
|
private final List<Arrow> targetArrows = new ArrayList<Arrow>();
|
||||||
private final List<Vector2[]> arcsOther = new ArrayList<Vector2[]>();
|
private final List<Arrow> pairArrows = new ArrayList<Arrow>();
|
||||||
|
|
||||||
public TargetingOverlay() {
|
public TargetingOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - this is called every repaint, regardless if card
|
private void refreshCombatArrows() {
|
||||||
// positions have changed or not. Could perform better if
|
combatArrows.clear();
|
||||||
// it checked for a state change.
|
|
||||||
private void assembleArcs() {
|
|
||||||
//List<VField> 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<Integer, Vector2> endpoints = new HashMap<Integer, Vector2>();
|
|
||||||
|
|
||||||
for (FCardPanel c : cardPanels) {
|
|
||||||
endpoints.put(c.getCard().getUniqueNumber(), c.getTargetingArrowOrigin());
|
|
||||||
}
|
|
||||||
|
|
||||||
final Combat combat = FControl.getGame().getCombat();
|
final Combat combat = FControl.getGame().getCombat();
|
||||||
|
|
||||||
// Work with all card panels currently visible
|
|
||||||
List<Card> visualized = new ArrayList<Card>();
|
|
||||||
for (FCardPanel c : cardPanels) {
|
|
||||||
Card card = c.getCard();
|
|
||||||
if (visualized.contains(card)) { continue; }
|
|
||||||
|
|
||||||
visualized.addAll(addArcsForCard(card, endpoints, combat));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Card> addArcsForCard(final Card c, final Map<Integer, Vector2> endpoints, final Combat combat) {
|
|
||||||
List<Card> cardsVisualized = new ArrayList<Card>();
|
|
||||||
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) {
|
if (combat != null) {
|
||||||
|
//connect each attacker with planeswalker it's attacking if applicable
|
||||||
for (Card planeswalker : combat.getDefendingPlaneswalkers()) {
|
for (Card planeswalker : combat.getDefendingPlaneswalkers()) {
|
||||||
List<Card> cards = combat.getAttackersOf(planeswalker);
|
for (Card attacker : combat.getAttackersOf(planeswalker)) {
|
||||||
for (Card pwAttacker : cards) {
|
combatArrows.add(new Arrow(attacker, planeswalker));
|
||||||
if (!planeswalker.equals(c) && !pwAttacker.equals(c)) { continue; }
|
|
||||||
arcsCombat.add(new Vector2[] {
|
|
||||||
endpoints.get(planeswalker.getUniqueNumber()),
|
|
||||||
endpoints.get(pwAttacker.getUniqueNumber())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Card attackingCard : combat.getAttackers()) {
|
|
||||||
List<Card> 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);
|
|
||||||
}
|
|
||||||
cardsVisualized.add(attackingCard);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cardsVisualized;
|
//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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawArcs(Graphics g, FSkinColor color, List<Vector2[]> arcs) {
|
private void refreshTargetArrows() {
|
||||||
for (Vector2[] p : arcs) {
|
targetArrows.clear();
|
||||||
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);
|
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<Card> pairedCards = new HashSet<Card>();
|
||||||
|
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) {
|
public void draw(final Graphics g) {
|
||||||
assembleArcs();
|
refreshPairArrows(); //TODO: Optimize so these don't need to be refreshed every render
|
||||||
|
if (!pairArrows.isEmpty()) {
|
||||||
if (arcsCombat.isEmpty() && arcsOther.isEmpty()) { return; }
|
FSkinColor color = FSkinColor.get(Colors.CLR_NORMAL_TARGETING_ARROW);
|
||||||
|
if (color.getAlpha() == 0) {
|
||||||
// Get arrow colors from the theme or use default colors if the theme does not have them defined
|
color = FSkinColor.get(Colors.CLR_ACTIVE).alphaColor(153f / 255f);
|
||||||
FSkinColor colorOther = FSkinColor.get(Colors.CLR_NORMAL_TARGETING_ARROW);
|
}
|
||||||
if (colorOther.getAlpha() == 0) {
|
for (Arrow arrow : pairArrows) {
|
||||||
colorOther = FSkinColor.get(Colors.CLR_ACTIVE).alphaColor(153f / 255f);
|
arrow.draw(g, color);
|
||||||
}
|
}
|
||||||
FSkinColor colorCombat = FSkinColor.get(Colors.CLR_COMBAT_TARGETING_ARROW);
|
|
||||||
if (colorCombat.getAlpha() == 0) {
|
|
||||||
colorCombat = FSkinColor.getStandardColor(new Color(1, 0, 0, 153 / 255f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawArcs(g, colorOther, arcsOther);
|
refreshCombatArrows();
|
||||||
drawArcs(g, colorCombat, arcsCombat);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.screens.match.views;
|
package forge.screens.match.views;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||||
|
import com.badlogic.gdx.math.Vector2;
|
||||||
|
|
||||||
import forge.Graphics;
|
import forge.Graphics;
|
||||||
import forge.assets.FSkin;
|
import forge.assets.FSkin;
|
||||||
@@ -34,6 +35,20 @@ public class VAvatar extends FDisplayObject {
|
|||||||
return true;
|
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
|
@Override
|
||||||
public void draw(Graphics g) {
|
public void draw(Graphics g) {
|
||||||
float w = getWidth();
|
float w = getWidth();
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ public abstract class VCardDisplayArea extends VDisplayArea {
|
|||||||
protected final List<Card> orderedCards = new ArrayList<Card>();
|
protected final List<Card> orderedCards = new ArrayList<Card>();
|
||||||
protected final List<CardAreaPanel> cardPanels = new ArrayList<CardAreaPanel>();
|
protected final List<CardAreaPanel> cardPanels = new ArrayList<CardAreaPanel>();
|
||||||
|
|
||||||
|
public Iterable<Card> getOrderedCards() {
|
||||||
|
return orderedCards;
|
||||||
|
}
|
||||||
|
|
||||||
public Iterable<CardAreaPanel> getCardPanels() {
|
public Iterable<CardAreaPanel> getCardPanels() {
|
||||||
return cardPanels;
|
return cardPanels;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import forge.util.Utils;
|
|||||||
public class VStack extends FDropDown {
|
public class VStack extends FDropDown {
|
||||||
public static final float CARD_WIDTH = Utils.AVG_FINGER_WIDTH;
|
public static final float CARD_WIDTH = Utils.AVG_FINGER_WIDTH;
|
||||||
public static final float CARD_HEIGHT = Math.round(CARD_WIDTH * FCardPanel.ASPECT_RATIO);
|
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 FSkinFont FONT = FSkinFont.get(11);
|
||||||
private static final float ALPHA_COMPOSITE = 0.5f;
|
private static final float ALPHA_COMPOSITE = 0.5f;
|
||||||
private static final TextRenderer textRenderer = new TextRenderer(true);
|
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
|
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 SpellAbilityStackInstance stackInstance;
|
||||||
private final boolean isTop;
|
private final boolean isTop;
|
||||||
private final Color foreColor, backColor;
|
private final Color foreColor, backColor;
|
||||||
@@ -141,6 +141,10 @@ public class VStack extends FDropDown {
|
|||||||
return Math.round(height);
|
return Math.round(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpellAbilityStackInstance getStackInstance() {
|
||||||
|
return stackInstance;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tap(float x, float y, int count) {
|
public boolean tap(float x, float y, int count) {
|
||||||
final Player player = stackInstance.getSpellAbility().getActivatingPlayer();
|
final Player player = stackInstance.getSpellAbility().getActivatingPlayer();
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public class FCardPanel extends FDisplayObject {
|
|||||||
public static final float TAPPED_ANGLE = -90;
|
public static final float TAPPED_ANGLE = -90;
|
||||||
public static final float ASPECT_RATIO = 3.5f / 2.5f;
|
public static final float ASPECT_RATIO = 3.5f / 2.5f;
|
||||||
public static final float PADDING = Utils.scaleMin(2);
|
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 Card card;
|
||||||
private boolean tapped;
|
private boolean tapped;
|
||||||
@@ -93,8 +95,8 @@ public class FCardPanel extends FDisplayObject {
|
|||||||
h = temp;
|
h = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
origin.x += left + w * 0.15f;
|
origin.x += left + w * TARGET_ORIGIN_FACTOR_X;
|
||||||
origin.y += top + h * 0.5f;
|
origin.y += top + h * TARGET_ORIGIN_FACTOR_Y;
|
||||||
|
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user