mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
- Update for the desktop Forge Targeting Overlay code: significantly improves performance of targeting overlay, gets rid of the serious and continuous CPU overload whenever targeting arrows are displayed (includes code contribution from nefigah).
This commit is contained in:
@@ -308,6 +308,9 @@ public final class CMatchUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<VField> getFieldViews() {
|
public List<VField> getFieldViews() {
|
||||||
|
if (view == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return view.getFieldViews();
|
return view.getFieldViews();
|
||||||
}
|
}
|
||||||
public VField getFieldViewFor(final PlayerView p) {
|
public VField getFieldViewFor(final PlayerView p) {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.GeneralPath;
|
||||||
@@ -48,6 +50,8 @@ import forge.toolbox.FSkin;
|
|||||||
import forge.toolbox.FSkin.SkinnedPanel;
|
import forge.toolbox.FSkin.SkinnedPanel;
|
||||||
import forge.view.FView;
|
import forge.view.FView;
|
||||||
import forge.view.arcane.CardPanel;
|
import forge.view.arcane.CardPanel;
|
||||||
|
import forge.view.arcane.CardPanelContainer;
|
||||||
|
import forge.view.arcane.util.CardPanelMouseListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Semi-transparent overlay panel. Should be used with layered panes.
|
* Semi-transparent overlay panel. Should be used with layered panes.
|
||||||
@@ -60,6 +64,8 @@ public class TargetingOverlay {
|
|||||||
private final List<CardPanel> cardPanels = new ArrayList<CardPanel>();
|
private final List<CardPanel> cardPanels = new ArrayList<CardPanel>();
|
||||||
private final List<Arc> arcsFoe = new ArrayList<Arc>();
|
private final List<Arc> arcsFoe = new ArrayList<Arc>();
|
||||||
private final List<Arc> arcsFriend = new ArrayList<Arc>();
|
private final List<Arc> arcsFriend = new ArrayList<Arc>();
|
||||||
|
private final ArcAssembler assembler = new ArcAssembler();
|
||||||
|
private final Set<Integer> stackItemIDs = new HashSet<Integer>();
|
||||||
|
|
||||||
private static class Arc {
|
private static class Arc {
|
||||||
private final int x1, y1, x2, y2;
|
private final int x1, y1, x2, y2;
|
||||||
@@ -75,6 +81,8 @@ public class TargetingOverlay {
|
|||||||
private final Set<CardView> cardsVisualized = new HashSet<CardView>();
|
private final Set<CardView> cardsVisualized = new HashSet<CardView>();
|
||||||
private CardPanel activePanel = null;
|
private CardPanel activePanel = null;
|
||||||
|
|
||||||
|
private long lastUpdated = System.currentTimeMillis();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Semi-transparent overlay panel. Should be used with layered panes.
|
* Semi-transparent overlay panel. Should be used with layered panes.
|
||||||
*/
|
*/
|
||||||
@@ -91,21 +99,33 @@ public class TargetingOverlay {
|
|||||||
return this.pnl;
|
return this.pnl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - this is called every repaint, regardless if card
|
// Though this is called on every repaint, we compare the last time it
|
||||||
// positions have changed or not. Could perform better if
|
// ran against the current time to prevent CPU usage from spiking from overly
|
||||||
// it checked for a state change. Doublestrike 28-09-12
|
// frequent updates. It is also called (without the timer) in response to
|
||||||
private void assembleArcs(final CombatView combat) {
|
// certain important UI events.
|
||||||
|
private boolean assembleArcs(final CombatView combat, boolean forceAssemble) {
|
||||||
|
if (forceAssemble || System.currentTimeMillis() - lastUpdated > 50) {
|
||||||
|
lastUpdated = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assembler.getIsListening() && matchUI != null && matchUI.getFieldViews() != null) {
|
||||||
|
assembler.setIsListening(true);
|
||||||
|
for (final VField f : matchUI.getFieldViews()) {
|
||||||
|
f.getTabletop().addLayoutListener(assembler);
|
||||||
|
f.getTabletop().addCardPanelMouseListener(assembler);
|
||||||
|
}
|
||||||
|
}
|
||||||
//List<VField> fields = VMatchUI.SINGLETON_INSTANCE.getFieldViews();
|
//List<VField> fields = VMatchUI.SINGLETON_INSTANCE.getFieldViews();
|
||||||
arcsFoe.clear();
|
arcsFoe.clear();
|
||||||
arcsFriend.clear();
|
arcsFriend.clear();
|
||||||
cardPanels.clear();
|
cardPanels.clear();
|
||||||
cardsVisualized.clear();
|
cardsVisualized.clear();
|
||||||
|
|
||||||
final StackInstanceTextArea activeStackItem = matchUI.getCStack().getView().getHoveredItem();
|
|
||||||
|
|
||||||
switch (matchUI.getCDock().getArcState()) {
|
switch (matchUI.getCDock().getArcState()) {
|
||||||
case OFF:
|
case OFF:
|
||||||
return;
|
return true;
|
||||||
case MOUSEOVER:
|
case MOUSEOVER:
|
||||||
// Draw only hovered card
|
// Draw only hovered card
|
||||||
activePanel = null;
|
activePanel = null;
|
||||||
@@ -119,7 +139,7 @@ public class TargetingOverlay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (activePanel == null && activeStackItem == null) { return; }
|
if (activePanel == null) { return true; }
|
||||||
break;
|
break;
|
||||||
case ON:
|
case ON:
|
||||||
// Draw all
|
// Draw all
|
||||||
@@ -130,20 +150,7 @@ public class TargetingOverlay {
|
|||||||
|
|
||||||
//final Point docOffsets = FView.SINGLETON_INSTANCE.getLpnDocument().getLocationOnScreen();
|
//final Point docOffsets = FView.SINGLETON_INSTANCE.getLpnDocument().getLocationOnScreen();
|
||||||
// Locations of arc endpoint, per card, with ID as primary key.
|
// Locations of arc endpoint, per card, with ID as primary key.
|
||||||
final Map<Integer, Point> endpoints = new HashMap<Integer, Point>();
|
final Map<Integer, Point> endpoints = getCardEndpoints();
|
||||||
|
|
||||||
Point cardLocOnScreen;
|
|
||||||
Point locOnScreen = this.getPanel().getLocationOnScreen();
|
|
||||||
|
|
||||||
for (CardPanel c : cardPanels) {
|
|
||||||
if (c.isShowing()) {
|
|
||||||
cardLocOnScreen = c.getCardLocationOnScreen();
|
|
||||||
endpoints.put(c.getCard().getId(), new Point(
|
|
||||||
(int) (cardLocOnScreen.getX() - locOnScreen.getX() + (float)c.getWidth() * CardPanel.TARGET_ORIGIN_FACTOR_X),
|
|
||||||
(int) (cardLocOnScreen.getY() - locOnScreen.getY() + (float)c.getHeight() * CardPanel.TARGET_ORIGIN_FACTOR_Y)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchUI.getCDock().getArcState() == ArcState.MOUSEOVER) {
|
if (matchUI.getCDock().getArcState() == ArcState.MOUSEOVER) {
|
||||||
// Only work with the active panel
|
// Only work with the active panel
|
||||||
@@ -161,8 +168,49 @@ public class TargetingOverlay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//draw arrow connecting active item on stack
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, Point> getCardEndpoints() {
|
||||||
|
final Map<Integer, Point> endpoints = new HashMap<Integer, Point>();
|
||||||
|
|
||||||
|
Point cardLocOnScreen;
|
||||||
|
Point locOnScreen = this.getPanel().getLocationOnScreen();
|
||||||
|
|
||||||
|
for (CardPanel c : cardPanels) {
|
||||||
|
if (c.isShowing()) {
|
||||||
|
cardLocOnScreen = c.getCardLocationOnScreen();
|
||||||
|
endpoints.put(c.getCard().getId(), new Point(
|
||||||
|
(int) (cardLocOnScreen.getX() - locOnScreen.getX() + (float)c.getWidth() * CardPanel.TARGET_ORIGIN_FACTOR_X),
|
||||||
|
(int) (cardLocOnScreen.getY() - locOnScreen.getY() + (float)c.getHeight() * CardPanel.TARGET_ORIGIN_FACTOR_Y)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assembleStackArrows() {
|
||||||
|
final StackInstanceTextArea activeStackItem = matchUI.getCStack().getView().getHoveredItem();
|
||||||
|
|
||||||
if (activeStackItem != null) {
|
if (activeStackItem != null) {
|
||||||
|
// Add event listeners to the stack item to repaint on mouse
|
||||||
|
// entry/exit, to clear up visual artifacts of our arrows
|
||||||
|
if (stackItemIDs.add(activeStackItem.hashCode())) {
|
||||||
|
activeStackItem.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(final MouseEvent e) {
|
||||||
|
assembleStackArrows();
|
||||||
|
FView.SINGLETON_INSTANCE.getFrame().repaint();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mouseExited(final MouseEvent e) {
|
||||||
|
assembleStackArrows();
|
||||||
|
FView.SINGLETON_INSTANCE.getFrame().repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final Map<Integer, Point> endpoints = getCardEndpoints();
|
||||||
|
Point locOnScreen = this.getPanel().getLocationOnScreen();
|
||||||
Point itemLocOnScreen = activeStackItem.getLocationOnScreen();
|
Point itemLocOnScreen = activeStackItem.getLocationOnScreen();
|
||||||
if (itemLocOnScreen != null) {
|
if (itemLocOnScreen != null) {
|
||||||
itemLocOnScreen.x += StackInstanceTextArea.CARD_WIDTH * CardPanel.TARGET_ORIGIN_FACTOR_X + StackInstanceTextArea.PADDING - locOnScreen.getX();
|
itemLocOnScreen.x += StackInstanceTextArea.CARD_WIDTH * CardPanel.TARGET_ORIGIN_FACTOR_X + StackInstanceTextArea.PADDING - locOnScreen.getX();
|
||||||
@@ -176,7 +224,7 @@ public class TargetingOverlay {
|
|||||||
}
|
}
|
||||||
for (PlayerView p : instance.getTargetPlayers()) {
|
for (PlayerView p : instance.getTargetPlayers()) {
|
||||||
Point point = getPlayerTargetingArrowPoint(p, locOnScreen);
|
Point point = getPlayerTargetingArrowPoint(p, locOnScreen);
|
||||||
if(point != null) {
|
if (point != null) {
|
||||||
addArc(point, itemLocOnScreen, activator.isOpponentOf(p));
|
addArc(point, itemLocOnScreen, activator.isOpponentOf(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +247,9 @@ public class TargetingOverlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addArc(Point end, Point start, boolean connectsFoes) {
|
private void addArc(Point end, Point start, boolean connectsFoes) {
|
||||||
if (start == null || end == null) { return; }
|
if (start == null || end == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (connectsFoes) {
|
if (connectsFoes) {
|
||||||
arcsFoe.add(new Arc(end, start));
|
arcsFoe.add(new Arc(end, start));
|
||||||
@@ -210,7 +260,9 @@ public class TargetingOverlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addArcsForCard(final CardView c, final Map<Integer, Point> endpoints, final CombatView combat) {
|
private void addArcsForCard(final CardView c, final Map<Integer, Point> endpoints, final CombatView combat) {
|
||||||
if (!cardsVisualized.add(c)) { return; } //don't add arcs for cards if card already visualized
|
if (!cardsVisualized.add(c)) {
|
||||||
|
return; //don't add arcs for cards if card already visualized
|
||||||
|
}
|
||||||
|
|
||||||
final CardView enchanting = c.getEnchantingCard();
|
final CardView enchanting = c.getEnchantingCard();
|
||||||
final CardView equipping = c.getEquipping();
|
final CardView equipping = c.getEquipping();
|
||||||
@@ -384,12 +436,20 @@ public class TargetingOverlay {
|
|||||||
if (overlaystate == ArcState.OFF) { return; }
|
if (overlaystate == ArcState.OFF) { return; }
|
||||||
|
|
||||||
// Arc drawing
|
// Arc drawing
|
||||||
|
boolean assembled = false;
|
||||||
final GameView gameView = matchUI.getGameView();
|
final GameView gameView = matchUI.getGameView();
|
||||||
if (gameView != null) {
|
if (gameView != null) {
|
||||||
assembleArcs(gameView.getCombat());
|
assembled = assembleArcs(gameView.getCombat(), false);
|
||||||
|
assembleStackArrows();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arcsFoe.isEmpty() && arcsFriend.isEmpty()) { return; }
|
if (arcsFoe.isEmpty() && arcsFriend.isEmpty()) {
|
||||||
|
if (assembled) {
|
||||||
|
// We still need to repaint to get rid of visual artifacts
|
||||||
|
FView.SINGLETON_INSTANCE.getFrame().repaint();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
@@ -407,7 +467,49 @@ public class TargetingOverlay {
|
|||||||
drawArcs(g2d, colorOther, arcsFriend);
|
drawArcs(g2d, colorOther, arcsFriend);
|
||||||
drawArcs(g2d, colorCombat, arcsFoe);
|
drawArcs(g2d, colorCombat, arcsFoe);
|
||||||
|
|
||||||
FView.SINGLETON_INSTANCE.getFrame().repaint(); // repaint the match UI
|
if (assembled) {
|
||||||
|
FView.SINGLETON_INSTANCE.getFrame().repaint(); // repaint the match UI
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A class for listening to CardPanelContainer events, so that we can redraw arcs in response
|
||||||
|
class ArcAssembler implements CardPanelContainer.LayoutEventListener, CardPanelMouseListener {
|
||||||
|
// An "initialized"-type variable is needed since we can't initialize on construction
|
||||||
|
// (because CardPanelContainers aren't ready at that point)
|
||||||
|
private boolean isListening = false;
|
||||||
|
public boolean getIsListening() { return isListening; }
|
||||||
|
public void setIsListening(boolean listening) { isListening = listening; }
|
||||||
|
|
||||||
|
private void assembleAndRepaint() {
|
||||||
|
final GameView gameView = matchUI.getGameView();
|
||||||
|
if (gameView != null) {
|
||||||
|
assembleArcs(gameView.getCombat(), true); // Force update despite timer
|
||||||
|
FView.SINGLETON_INSTANCE.getFrame().repaint(); // repaint the match UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void doingLayout() {
|
||||||
|
assembleAndRepaint();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mouseOver(CardPanel panel, MouseEvent evt) {
|
||||||
|
assembleAndRepaint();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mouseOut(CardPanel panel, MouseEvent evt) {
|
||||||
|
assembleAndRepaint();
|
||||||
|
}
|
||||||
|
// We don't need the other mouse events the interface provides; stub them out
|
||||||
|
@Override
|
||||||
|
public void mouseLeftClicked(CardPanel panel, MouseEvent evt) {}
|
||||||
|
@Override
|
||||||
|
public void mouseRightClicked(CardPanel panel, MouseEvent evt) {}
|
||||||
|
@Override
|
||||||
|
public void mouseDragStart(CardPanel dragPanel, MouseEvent evt) {}
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(CardPanel dragPanel, int dragOffsetX, int dragOffsetY, MouseEvent evt) {}
|
||||||
|
@Override
|
||||||
|
public void mouseDragEnd(CardPanel dragPanel, MouseEvent evt) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,8 @@ public class CardArea extends CardPanelContainer implements CardPanelMouseListen
|
|||||||
this.getParent().invalidate();
|
this.getParent().invalidate();
|
||||||
this.getParent().validate();
|
this.getParent().validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.doLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public abstract class CardPanelContainer extends SkinnedPanel {
|
|||||||
private int intialMouseDragX = -1, intialMouseDragY;
|
private int intialMouseDragX = -1, intialMouseDragY;
|
||||||
private boolean dragEnabled;
|
private boolean dragEnabled;
|
||||||
|
|
||||||
|
private final List<LayoutEventListener> layoutListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
public CardPanelContainer(final CMatchUI matchUI, final FScrollPane scrollPane) {
|
public CardPanelContainer(final CMatchUI matchUI, final FScrollPane scrollPane) {
|
||||||
this.matchUI = matchUI;
|
this.matchUI = matchUI;
|
||||||
this.scrollPane = scrollPane;
|
this.scrollPane = scrollPane;
|
||||||
@@ -293,6 +295,7 @@ public abstract class CardPanelContainer extends SkinnedPanel {
|
|||||||
fromPanel.dispose();
|
fromPanel.dispose();
|
||||||
getCardPanels().remove(fromPanel);
|
getCardPanels().remove(fromPanel);
|
||||||
remove(fromPanel);
|
remove(fromPanel);
|
||||||
|
this.doLayout();
|
||||||
invalidate();
|
invalidate();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
@@ -418,4 +421,21 @@ public abstract class CardPanelContainer extends SkinnedPanel {
|
|||||||
public void setMouseDragPanel(final CardPanel mouseDragPanel0) {
|
public void setMouseDragPanel(final CardPanel mouseDragPanel0) {
|
||||||
this.mouseDragPanel = mouseDragPanel0;
|
this.mouseDragPanel = mouseDragPanel0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doLayout() {
|
||||||
|
// Inform listeners we're doing layout
|
||||||
|
for (LayoutEventListener listener : layoutListeners) {
|
||||||
|
listener.doingLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface LayoutEventListener {
|
||||||
|
void doingLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLayoutListener(LayoutEventListener listener) {
|
||||||
|
this.layoutListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -335,6 +335,8 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
|
|||||||
this.revalidate();
|
this.revalidate();
|
||||||
positionAllCards(lastTemplate);
|
positionAllCards(lastTemplate);
|
||||||
repaint();
|
repaint();
|
||||||
|
|
||||||
|
super.doLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position all card panels
|
// Position all card panels
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ KrazyTheFox
|
|||||||
Marek14
|
Marek14
|
||||||
mcrawford620
|
mcrawford620
|
||||||
Myrd
|
Myrd
|
||||||
|
nefigah
|
||||||
pfps
|
pfps
|
||||||
Sloth
|
Sloth
|
||||||
slyfox7777777
|
slyfox7777777
|
||||||
|
|||||||
Reference in New Issue
Block a user