Merge branch 'master' into 'master'

Select cards from any Zone that can be shown and do arrangeForScry by popping up library and moving cards (in desktop GUI); Pass min/max card selections through to GUI

See merge request core-developers/forge!1260
This commit is contained in:
Michael Kamensky
2019-01-11 17:08:50 +00:00
19 changed files with 613 additions and 104 deletions

View File

@@ -29,7 +29,7 @@ import forge.item.PaperCard;
import forge.model.FModel;
import forge.screens.match.CMatchUI;
import forge.toolbox.FOptionPane;
import forge.view.arcane.ListCardArea;
public class GuiChoose {
@@ -285,5 +285,31 @@ public class GuiChoose {
return null;
}
public static List<Card> manipulateCardList(final CMatchUI gui, final String title, final List<Card> cards, final List<Card> manipulable,
final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
final Callable<List<Card>> callable = new Callable<List<Card>>() {
@Override
public List<Card> call() throws Exception {
ListCardArea tempArea = new ListCardArea(gui,title,cards,manipulable,toTop,toBottom,toAnywhere);
// tempArea.pack();
// window? tempArea.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
tempArea.show();
// tempArea.dispose();
//try { Thread.sleep(1000); } catch(InterruptedException ex) { }
final List<Card> cardList = tempArea.getCardList();
return cardList;
}
};
final FutureTask<List<Card>> ft = new FutureTask<List<Card>>(callable);
FThreads.invokeInEdtAndWait(ft);
try {
List<Card> result = ft.get();
return result;
} catch (final Exception e) { // we have waited enough
e.printStackTrace();
}
return null;
}
}

View File

@@ -51,6 +51,7 @@ import forge.deck.Deck;
import forge.deckchooser.FDeckViewer;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.combat.CombatView;
import forge.game.phase.PhaseType;
@@ -101,6 +102,7 @@ import forge.util.gui.SOptionPane;
import forge.view.FView;
import forge.view.arcane.CardPanel;
import forge.view.arcane.FloatingCardArea;
import forge.match.input.*;
/**
* Constructs instance of match UI controller, used as a single point of
@@ -395,7 +397,8 @@ public final class CMatchUI
break;
case Hand:
updateHand = true;
//$FALL-THROUGH$
updateZones = true;
break;
default:
updateZones = true;
FloatingCardArea.refresh(owner, zone);
@@ -424,6 +427,57 @@ public final class CMatchUI
}
}
@Override
public Iterable<PlayerZoneUpdate> tempShowZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
for (final PlayerZoneUpdate update : zonesToUpdate) {
final PlayerView player = update.getPlayer();
for (final ZoneType zone : update.getZones()) {
switch (zone) {
case Battlefield: // always shown
break;
case Hand: // controller hand always shown
if (controller != player) {
FloatingCardArea.show(this,player,zone);
}
break;
case Library:
case Graveyard:
case Exile:
case Flashback:
case Command:
FloatingCardArea.show(this,player,zone);
break;
default:
break;
}
}
}
return zonesToUpdate; //pfps should return only the newly shown zones
}
@Override
public void hideZones(final PlayerView controller, final Iterable<PlayerZoneUpdate> zonesToUpdate) {
for (final PlayerZoneUpdate update : zonesToUpdate) {
final PlayerView player = update.getPlayer();
for (final ZoneType zone : update.getZones()) {
switch (zone) {
case Battlefield: // always shown
break;
case Hand: // the controller's hand should never be temporarily shown, but ...
case Library:
case Graveyard:
case Exile:
case Flashback:
case Command:
FloatingCardArea.hide(this,player,zone);
break;
default:
break;
}
}
}
}
// Player's mana pool changes
@Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
@@ -465,6 +519,7 @@ public final class CMatchUI
}
break;
default:
FloatingCardArea.refresh(c.getController(),zone); // in case the card is visible in the zone
break;
}
}
@@ -942,11 +997,16 @@ public final class CMatchUI
}
@Override
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final int min, final int max, final DelayedReveal delayedReveal) {
if (delayedReveal != null) {
reveal(delayedReveal.getMessagePrefix(), delayedReveal.getCards()); //TODO: Merge this into search dialog
}
return (List<GameEntityView>) order(title,"Selected", 0, optionList.size(), optionList, null, null, false);
return (List<GameEntityView>) order(title,"Selected", min, max, optionList, null, null, false);
}
@Override
public List<Card> manipulateCardList(final String title, final List<Card> cards, final List<Card> manipulable, final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
return GuiChoose.manipulateCardList(this, title, cards, manipulable, toTop, toBottom, toAnywhere);
}
@Override

View File

@@ -246,8 +246,8 @@ public class CardArea extends CardPanelContainer implements CardPanelMouseListen
dragPanel.setDisplayEnabled(false);
CardPanel.setDragAnimationPanel(new CardPanel(dragPanel.getMatchUI(), dragPanel.getCard()));
final JFrame frame = (JFrame) SwingUtilities.windowForComponent(this);
final JLayeredPane layeredPane = frame.getLayeredPane();
final RootPaneContainer frame = (RootPaneContainer) SwingUtilities.windowForComponent(this);
final JLayeredPane layeredPane = frame.getLayeredPane();
layeredPane.add(CardPanel.getDragAnimationPanel());
layeredPane.moveToFront(CardPanel.getDragAnimationPanel());
final Point p = SwingUtilities.convertPoint(this, this.mouseDragStartX, this.mouseDragStartY, layeredPane);

View File

@@ -182,6 +182,10 @@ public abstract class CardPanelContainer extends SkinnedPanel {
});
}
protected boolean cardPanelDraggable(final CardPanel panel) {
return true;
}
private MouseMotionListener setupMotionMouseListener() {
final MouseMotionListener mml = new MouseMotionListener() {
@Override
@@ -207,20 +211,23 @@ public abstract class CardPanelContainer extends SkinnedPanel {
if (panel != mouseDownPanel) {
return;
}
if (intialMouseDragX == -1) {
intialMouseDragX = x;
intialMouseDragY = y;
return;
}
if ((Math.abs(x - intialMouseDragX) < CardPanelContainer.DRAG_SMUDGE)
if (cardPanelDraggable(panel)) { // allow for non-draggable cards
if (intialMouseDragX == -1) {
intialMouseDragX = x;
intialMouseDragY = y;
return;
}
if ((Math.abs(x - intialMouseDragX) < CardPanelContainer.DRAG_SMUDGE)
&& (Math.abs(y - intialMouseDragY) < CardPanelContainer.DRAG_SMUDGE)) {
return;
}
mouseDownPanel = null;
setMouseDragPanel(panel);
mouseDragOffsetX = panel.getX() - intialMouseDragX;
mouseDragOffsetY = panel.getY() - intialMouseDragY;
mouseDragStart(getMouseDragPanel(), evt);
return;
}
mouseDownPanel = null;
setMouseDragPanel(panel);
mouseDragOffsetX = panel.getX() - intialMouseDragX;
mouseDragOffsetY = panel.getY() - intialMouseDragY;
mouseDragStart(getMouseDragPanel(), evt);
}
}
@Override
@@ -453,4 +460,4 @@ public abstract class CardPanelContainer extends SkinnedPanel {
this.layoutListeners.add(listener);
}
}
}

View File

@@ -69,6 +69,10 @@ public class FloatingCardArea extends CardArea {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.showWindow();
}
public static void hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final FloatingCardArea cardArea = _init(matchUI, player, zone);
cardArea.hideWindow();
}
private static FloatingCardArea _init(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) {
final int key = getKey(player, zone);
FloatingCardArea cardArea = floatingAreas.get(key);
@@ -205,6 +209,11 @@ public class FloatingCardArea extends CardArea {
window.setFocusableWindowState(false); // should probably do this earlier
window.setVisible(true);
}
private void hideWindow() {
onShow();
window.setFocusableWindowState(false); // should probably do this earlier
window.setVisible(false);
}
private void showOrHideWindow() {
onShow();
window.setFocusableWindowState(false); // should probably do this earlier

View File

@@ -0,0 +1,314 @@
/*
* Forge: Play Magic: the Gathering.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.view.arcane;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import forge.Singletons;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.gui.framework.SDisplayUtil;
import forge.model.FModel;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.CMatchUI;
import forge.view.arcane.util.CardPanelMouseAdapter;
import forge.toolbox.FScrollPane;
import forge.toolbox.MouseTriggerEvent;
import forge.view.FFrame;
import forge.view.FDialog;
import forge.toolbox.FButton;
public class ListCardArea extends CardArea {
private static final String COORD_DELIM = ",";
private static final ForgePreferences prefs = FModel.getPreferences();
public void show() {
this.showWindow();
}
public void hide() {
this.hideWindow();
}
private ArrayList<Card> cardList;
private ArrayList<Card> moveableCards;
private boolean toTop, toBottom, toAnywhere;
private String title;
private FPref locPref;
private boolean hasBeenShown = false, locLoaded;
private static ListCardArea storedArea;
private final FButton doneButton;
public ListCardArea(final CMatchUI matchUI, final String title0, final List<Card> cardList0, final List<Card> moveableCards0, final boolean toTop0, final boolean toBottom0, final boolean toAnywhere0) {
super(matchUI, new FScrollPane(false, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER));
window.add(getScrollPane(),"grow, push");
try { Thread.sleep(1000); } catch(InterruptedException ex) { }
getScrollPane().setViewportView(this);
setOpaque(false);
doneButton = new FButton("Done");
doneButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) { window.setVisible(false); }
});
window.add(doneButton,BorderLayout.SOUTH);
cardList = new ArrayList<Card>(cardList0); // this is modified - pfps - is there a better way?
moveableCards = new ArrayList<Card>(moveableCards0);
title = title0;
toTop = toTop0;
toBottom = toBottom0;
toAnywhere = toAnywhere0;
this.setDragEnabled(true);
this.setVertical(true);
storedArea = this;
}
public List<Card> getCardList() {
return cardList;
}
@SuppressWarnings("serial")
// private SkinnedFrame window = new SkinnedFrame() {
private final FDialog window = new FDialog(true, true, "0") {
@Override
public void setLocationRelativeTo(Component c) {
super.setLocationRelativeTo(c);
}
@Override
public void setVisible(boolean b0) {
if (isVisible() == b0) { return; }
if (b0) {
refresh();
}
super.setVisible(b0);
}
};
private void showWindow() {
onShow();
window.setFocusableWindowState(true);
window.setVisible(true);
}
private void hideWindow() {
onShow();
window.setFocusableWindowState(false); // should probably do this earlier
window.setVisible(false);
}
private void onShow() {
if (!hasBeenShown) {
loadLocation();
this.addCardPanelMouseListener(new CardPanelMouseAdapter() {
@Override
public void mouseDragEnd(final CardPanel dragPanel, final MouseEvent evt) {
dragEnd(dragPanel);
}
});
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(final KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER:
doneButton.doClick();
break;
default:
break;
}
}
});
}
}
// is this a valid place to move the card?
private boolean validIndex(final Card card, final int index) {
if (toAnywhere) { return true; }
int oldIndex = cardList.indexOf(card);
boolean topMove = true;
for(int i=0; i<index+(oldIndex<index?1:0); i++) {
if (!moveableCards.contains(cardList.get(i))) { topMove=false; break; }
}
if (toTop && topMove) { return true; }
boolean bottomMove = true;
for(int i=index+1-(oldIndex>index?1:0); i<cardList.size(); i++) {
if (!moveableCards.contains(cardList.get(i))) { bottomMove=false; break; }
}
if (toBottom && bottomMove) { return true; }
return false;
}
protected Card panelToCard(final CardPanel panel) { //pfps there must be a better way
final CardView panelView = panel.getCard();
Card panelCard = null;
for ( Card card : cardList ) { if ( panelView == card.getView() ) { panelCard = card; } }
return panelCard;
}
@Override
protected boolean cardPanelDraggable(final CardPanel panel) {
return moveableCards.contains(panelToCard(panel));
}
private void dragEnd(final CardPanel dragPanel) {
// if drag is not allowed, don't move anything
final Card dragCard = panelToCard(dragPanel);
if (moveableCards.contains(dragCard)) {
//update index of dragged card in hand zone to match new index within hand area
final int index = getCardPanels().indexOf(dragPanel);
if (validIndex(dragCard,index)) {
synchronized (cardList) {
cardList.remove(dragCard);
cardList.add(index, dragCard);
}
}
}
refresh();
}
private void loadLocation() {
if (locPref != null) {
String value = prefs.getPref(locPref);
if (value.length() > 0) {
String[] coords = value.split(COORD_DELIM);
if (coords.length == 4) {
try {
int x = Integer.parseInt(coords[0]);
int y = Integer.parseInt(coords[1]);
int w = Integer.parseInt(coords[2]);
int h = Integer.parseInt(coords[3]);
//ensure the window is accessible
int centerX = x + w / 2;
int centerY = y + h / 2;
Rectangle screenBounds = SDisplayUtil.getScreenBoundsForPoint(new Point(centerX, centerY));
if (centerX < screenBounds.x) {
x = screenBounds.x;
}
else if (centerX > screenBounds.x + screenBounds.width) {
x = screenBounds.x + screenBounds.width - w;
if (x < screenBounds.x) {
x = screenBounds.x;
}
}
if (centerY < screenBounds.y) {
y = screenBounds.y;
}
else if (centerY > screenBounds.y + screenBounds.height) {
y = screenBounds.y + screenBounds.height - h;
if (y < screenBounds.y) {
y = screenBounds.y;
}
}
window.setBounds(x, y, w, h);
locLoaded = true;
return;
}
catch (Exception ex) {
ex.printStackTrace();
}
}
prefs.setPref(locPref, ""); //clear value if invalid
prefs.save();
}
}
//fallback default size
FFrame mainFrame = Singletons.getView().getFrame();
window.setSize(mainFrame.getWidth() / 5, mainFrame.getHeight() / 2);
}
public void refresh() {
List<CardPanel> cardPanels = new ArrayList<CardPanel>();
// FCollectionView<Card> cards = new FCollection<Card>(cardList);
if (cardList != null) {
for (final Card card : cardList) {
CardPanel cardPanel = getCardPanel(card.getId());
if (cardPanel == null) {
cardPanel = new CardPanel(getMatchUI(), card.getView());
cardPanel.setDisplayEnabled(true);
}
else {
cardPanel.setCard(card.getView()); //ensure card view updated
}
cardPanels.add(cardPanel);
}
}
boolean hadCardPanels = getCardPanels().size() > 0;
setCardPanels(cardPanels);
window.setTitle(String.format(title, cardPanels.size()));
//if window had cards and now doesn't, hide window
//(e.g. cast final card from Flashback zone)
if (hadCardPanels && cardPanels.size() == 0) {
window.setVisible(false);
}
}
@Override
public void doLayout() {
// if (window.isResizing()) {
// //delay layout slightly to reduce flicker during window resize
// layoutTimer.restart();
// }
//else {
finishDoLayout();
//}
}
private final Timer layoutTimer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
layoutTimer.stop();
finishDoLayout();
}
});
private void finishDoLayout() {
super.doLayout();
}
@Override
public final void mouseOver(final CardPanel panel, final MouseEvent evt) {
getMatchUI().setCard(panel.getCard(), evt.isShiftDown());
super.mouseOver(panel, evt);
}
@Override
public final void mouseLeftClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseLeftClicked(panel, evt);
}
@Override
public final void mouseRightClicked(final CardPanel panel, final MouseEvent evt) {
getMatchUI().getGameController().selectCard(panel.getCard(), null, new MouseTriggerEvent(evt));
super.mouseRightClicked(panel, evt);
}
}

View File

@@ -174,7 +174,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) {
public <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) {
// this isn't used
return null;
}
@@ -624,7 +624,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, Player decider) {
public List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider) {
// this isn't used
return null;
}