GUI: improved cards with transform ability:

* added transform button to permanents on battlefield to view another card side;
* fixed wrong popup hints in some use cases (example: alternative side view by mouse wheel down, #8433);
This commit is contained in:
Oleg Agafonov 2021-11-05 23:16:28 +04:00
parent 767fd89199
commit 1f381cffe5
8 changed files with 91 additions and 104 deletions

View file

@ -309,6 +309,7 @@ public class TestCardRenderDialog extends MageDialog {
//* //test card icons //* //test card icons
cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears
cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 3, 3, 1, false, additionalIcons)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, additionalIcons)); // Hinterland Drake cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, additionalIcons)); // Hinterland Drake
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, additionalIcons)); // Kathari Remnant cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, additionalIcons)); // Kathari Remnant
cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, additionalIcons)); // Cosima, God of the Voyage cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, additionalIcons)); // Cosima, God of the Voyage

View file

@ -499,7 +499,7 @@ public class MageActionCallback implements ActionCallback {
} }
popupTextWindowOpen = true; popupTextWindowOpen = true;
Image image = cardPanel.getImage(); Image image = cardPanel.getImage();
displayCardInfo(cardPanel, image, bigCard); displayCardInfo(cardPanel.getOriginal(), image, bigCard);
} }
} }
} else { } else {
@ -671,7 +671,9 @@ public class MageActionCallback implements ActionCallback {
popupContainer.setLocation(location); popupContainer.setLocation(location);
popupContainer.setVisible(true); popupContainer.setVisible(true);
// popup hint mode
Image image = null; Image image = null;
CardView displayCard = cardPanel.getOriginal();
switch (enlargeMode) { switch (enlargeMode) {
case COPY: case COPY:
if (cardView instanceof PermanentView) { if (cardView instanceof PermanentView) {
@ -687,6 +689,7 @@ public class MageActionCallback implements ActionCallback {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()); image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal());
} else { } else {
image = ImageCache.getImageOriginalAlternateName(cardView); image = ImageCache.getImageOriginalAlternateName(cardView);
displayCard = displayCard.getSecondCardFace();
} }
} }
break; break;
@ -697,7 +700,7 @@ public class MageActionCallback implements ActionCallback {
image = cardPanel.getImage(); image = cardPanel.getImage();
} }
// shows the card in the popup Container // shows the card in the popup Container
displayCardInfo(cardPanel, image, (BigCard) cardPreviewPane); displayCardInfo(displayCard, image, (BigCard) cardPreviewPane);
} else { } else {
logger.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName()); logger.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName());
@ -709,21 +712,21 @@ public class MageActionCallback implements ActionCallback {
}); });
} }
private void displayCardInfo(MageCard mageCard, Image image, BigCard bigCard) { private void displayCardInfo(CardView card, Image image, BigCard bigCard) {
if (image instanceof BufferedImage) { if (image instanceof BufferedImage) {
// XXX: scaled to fit width // XXX: scaled to fit width
bigCard.setCard(mageCard.getOriginal().getId(), enlargeMode, image, mageCard.getOriginal().getRules(), mageCard.getOriginal().isToRotate()); bigCard.setCard(card.getId(), enlargeMode, image, card.getRules(), card.isToRotate());
// if it's an ability, show only the ability text as overlay // if it's an ability, show only the ability text as overlay
if (mageCard.getOriginal().isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) { if (card.isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) {
bigCard.showTextComponent(); bigCard.showTextComponent();
} else { } else {
bigCard.hideTextComponent(); bigCard.hideTextComponent();
} }
} else { } else {
JXPanel panel = GuiDisplayUtil.getDescription(mageCard.getOriginal(), bigCard.getWidth(), bigCard.getHeight()); JXPanel panel = GuiDisplayUtil.getDescription(card, bigCard.getWidth(), bigCard.getHeight());
panel.setVisible(true); panel.setVisible(true);
bigCard.hideTextComponent(); bigCard.hideTextComponent();
bigCard.addJXPanel(mageCard.getOriginal().getId(), panel); bigCard.addJXPanel(card.getId(), panel);
} }
enlargeredViewOpened = new Date(); enlargeredViewOpened = new Date();
} }

View file

@ -49,12 +49,10 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
private static final float ROT_CENTER_TO_BOTTOM_CORNER = 0.7071067811865475244008443621048f; private static final float ROT_CENTER_TO_BOTTOM_CORNER = 0.7071067811865475244008443621048f;
private CardView gameCard; // current card side, can be different for double faces cards (it's a gui sides, not mtg - so mdf cards will have second side too) private CardView gameCard; // current card side, can be different for double faces cards (it's a gui sides, not mtg - so mdf cards will have second side too)
private CardView cardSideMain = null; // for gui card side switch
private CardView cardSideOther = null; // for gui card side switch
private CardView updateCard; private CardView updateCard;
// if null then gameCard contains main card
// if not null then gameCard contains second side
private CardView temporary;
private double tappedAngle = 0; private double tappedAngle = 0;
private double flippedAngle = 0; private double flippedAngle = 0;
@ -98,7 +96,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
public double transformAngle = 1; public double transformAngle = 1;
private boolean transformed; private boolean guiTransformed;
private boolean animationInProgress = false; private boolean animationInProgress = false;
private Container cardContainer; private Container cardContainer;
@ -115,6 +113,8 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Store away params // Store away params
this.setGameCard(newGameCard); this.setGameCard(newGameCard);
this.setGameCardSides(newGameCard);
this.callback = callback; this.callback = callback;
this.gameId = gameId; this.gameId = gameId;
this.needFullPermanentRender = needFullPermanentRender; this.needFullPermanentRender = needFullPermanentRender;
@ -153,16 +153,24 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Create the day night button // Create the day night button
dayNightButton = new JButton(""); dayNightButton = new JButton("");
dayNightButton.setSize(32, 32); dayNightButton.setSize(32, 32);
dayNightButton.setToolTipText("This permanent is a double faced card. To see the back face card, push this button or turn mouse wheel down while hovering with the mouse pointer over the permanent."); dayNightButton.setToolTipText("This permanent is a double faced card. To see the another face card, push this button or move mouse wheel down while hovering over it.");
BufferedImage day = ImageManagerImpl.instance.getDayImage(); BufferedImage day = ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(day)); dayNightButton.setIcon(new ImageIcon(day));
dayNightButton.addActionListener(e -> { dayNightButton.addActionListener(e -> {
// if card is being rotated, ignore action performed // if card is rotating then ignore it
// if card is tapped, no visual transforming is possible (implementation limitation) if (animationInProgress) {
// if card is permanent, it will be rotated by Mage, so manual rotate should be possible
if (animationInProgress || isTapped() || isPermanent) {
return; return;
} }
// if card is tapped then no visual transforming is possible, so switch it immediately
if (isTapped()) {
// toggle without animation
this.getTopPanelRef().toggleTransformed();
this.getTopPanelRef().repaint();
return;
}
// normal animation
Animation.transformCard(this); Animation.transformCard(this);
}); });
@ -470,7 +478,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public final boolean isTapped() { public final boolean isTapped() {
if (isPermanent) { if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isTapped(); return ((PermanentView) getGameCard()).isTapped();
} }
return false; return false;
@ -478,7 +486,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public final boolean isFlipped() { public final boolean isFlipped() {
if (isPermanent) { if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isFlipped(); return ((PermanentView) getGameCard()).isFlipped();
} }
return false; return false;
@ -486,15 +494,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public final boolean isTransformed() { public final boolean isTransformed() {
if (isPermanent) { return this.guiTransformed;
if (getGameCard().isTransformed()) {
return !this.transformed;
} else {
return this.transformed;
}
} else {
return this.transformed;
}
} }
@Override @Override
@ -521,7 +521,9 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
return; return;
} }
if (transformed && card.equals(this.temporary)) { // card param can be any card side (example: user switch a card side by transform button)
if (guiTransformed && card.equals(this.cardSideMain)) {
// update can be called from different places (after transform click, after selection change, etc) // update can be called from different places (after transform click, after selection change, etc)
// if card temporary transformed before (by icon click) then do not update full data (as example, after selection changed) // if card temporary transformed before (by icon click) then do not update full data (as example, after selection changed)
this.isChoosable = card.isChoosable(); this.isChoosable = card.isChoosable();
@ -542,7 +544,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
AudioManager.playTapPermanent(); AudioManager.playTapPermanent();
} }
boolean needsTranforming = isTransformed() != card.isTransformed(); boolean needsTranforming = isTransformed() != card.isTransformed();
if (needsTranforming) { if (needsTranforming && !animationInProgress) {
Animation.transformCard(this); Animation.transformCard(this);
} }
} }
@ -558,6 +560,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Set the new card // Set the new card
this.setGameCard(card); this.setGameCard(card);
this.setGameCardSides(card);
// Update tooltip text // Update tooltip text
String cardType = getType(card); String cardType = getType(card);
@ -571,13 +574,13 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Update transform circle // Update transform circle
if (card.canTransform()) { if (card.canTransform()) {
BufferedImage transformIcon; BufferedImage transformIcon;
if (isTransformed() || card.isTransformed()) { if (isTransformed() || card.isTransformed()) { // wtf
transformIcon = ImageManagerImpl.instance.getNightImage(); transformIcon = ImageManagerImpl.instance.getNightImage();
} else { } else {
transformIcon = ImageManagerImpl.instance.getDayImage(); transformIcon = ImageManagerImpl.instance.getDayImage();
} }
if (dayNightButton != null) { if (dayNightButton != null) {
dayNightButton.setVisible(!isPermanent); dayNightButton.setVisible(true); // show T button for any cards and permanents
dayNightButton.setIcon(new ImageIcon(transformIcon)); dayNightButton.setIcon(new ImageIcon(transformIcon));
} }
} }
@ -585,13 +588,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public CardView getOriginal() { public CardView getOriginal() {
if (this.temporary == null) { return this.cardSideMain;
// current side: main, return: main
return this.getGameCard();
} else {
// current side: second, return: main
return this.temporary;
}
} }
@Override @Override
@ -827,16 +824,14 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public PermanentView getOriginalPermanent() { public PermanentView getOriginalPermanent() {
// used in battlefield drawing, sorting and other GUI things
// users can switch current permanent to another side, but you must return original permanent here all the time
if (isPermanent) { if (isPermanent) {
return (PermanentView) this.getGameCard(); return (PermanentView) this.cardSideMain;
} }
throw new IllegalStateException("Is not permanent."); throw new IllegalStateException("Is not permanent.");
} }
public void setTransformed(boolean transformed) {
this.transformed = transformed;
}
private void copySelections(CardView source, CardView dest) { private void copySelections(CardView source, CardView dest) {
if (source != null && dest != null) { if (source != null && dest != null) {
dest.setSelected(source.isSelected()); dest.setSelected(source.isSelected());
@ -847,39 +842,30 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override @Override
public void toggleTransformed() { public void toggleTransformed() {
this.transformed = !this.transformed; // VIEW mode (user can change card side at any time by n/d button)
if (transformed) { this.guiTransformed = !this.guiTransformed;
// show night card
if (dayNightButton != null) { // if transformbable card is copied, button can be null if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage night = ImageManagerImpl.instance.getNightImage(); BufferedImage image = this.guiTransformed ? ImageManagerImpl.instance.getNightImage() : ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(night)); dayNightButton.setIcon(new ImageIcon(image));
} }
if (this.getGameCard().getSecondCardFace() == null) {
// switch card data
if (this.guiTransformed) {
// main side -> alternative side
if (this.cardSideOther == null) {
logger.error("no second side for card to transform!"); logger.error("no second side for card to transform!");
return; return;
} }
if (!isPermanent) { // use only for custom transformation (when pressing day-night button) copySelections(this.cardSideMain, this.cardSideOther);
copySelections(this.getGameCard(), this.getGameCard().getSecondCardFace()); update(this.cardSideOther);
this.setTemporary(this.getGameCard()); this.getGameCard().setAlternateName(this.cardSideMain.getName());
update(this.getGameCard().getSecondCardFace());
}
} else { } else {
// show day card // alternative side -> main side
if (dayNightButton != null) { // if transformbable card is copied, button can be null copySelections(this.cardSideOther, this.cardSideMain);
BufferedImage day = ImageManagerImpl.instance.getDayImage(); update(this.cardSideMain);
dayNightButton.setIcon(new ImageIcon(day)); this.getGameCard().setAlternateName(this.cardSideOther.getName());
} }
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
copySelections(this.getGameCard().getSecondCardFace(), this.getGameCard());
update(this.getTemporary());
this.setTemporary(null);
}
}
// switch card names for render
String temp = this.getGameCard().getAlternateName();
this.getGameCard().setAlternateName(this.getGameCard().getOriginalName());
this.getGameCard().setOriginalName(temp);
updateArtImage(); updateArtImage();
} }
@ -939,6 +925,31 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
this.gameCard = gameCard; this.gameCard = gameCard;
} }
private void setGameCardSides(CardView gameCard) {
if (this.cardSideMain == null) {
// new card
this.cardSideMain = gameCard;
this.cardSideOther = gameCard.getSecondCardFace();
} else {
// updated card
if (this.cardSideMain.getName().equals(gameCard.getName())) {
// from main side
this.cardSideMain = gameCard;
this.cardSideOther = gameCard.getSecondCardFace();
} else {
// from other side
this.cardSideOther = gameCard;
}
}
// fix other side: if it's a night side permanent then the main side info must be extracted
if (this.cardSideOther != null
&& this.cardSideOther.getName().equals(this.cardSideMain.getName())
&& this.cardSideMain instanceof PermanentView) {
this.cardSideOther = ((PermanentView) this.cardSideMain).getOriginal();
}
}
public CardView getUpdateCard() { public CardView getUpdateCard() {
return updateCard; return updateCard;
} }
@ -947,14 +958,6 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
this.updateCard = updateCard; this.updateCard = updateCard;
} }
public CardView getTemporary() {
return temporary;
}
public void setTemporary(CardView temporary) {
this.temporary = temporary;
}
public double getTappedAngle() { public double getTappedAngle() {
return tappedAngle; return tappedAngle;
} }
@ -986,6 +989,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
needLocation.getCardHeight() needLocation.getCardHeight()
); );
Rectangle animatedRect = MageLayer.animateCoords(this, normalRect); Rectangle animatedRect = MageLayer.animateCoords(this, normalRect);
return animatedRect.contains(x, y); return animatedRect.contains(x, y);
} }

View file

@ -774,7 +774,7 @@ public class CardPanelRenderModeImage extends CardPanel {
private BufferedImage getFaceDownImage() { private BufferedImage getFaceDownImage() {
// TODO: add download default images // TODO: add download default images
if (isPermanent()) { if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) { if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage(); return ImageCache.getMorphImage();
} else { } else {

View file

@ -409,7 +409,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
private BufferedImage getFaceDownImage() { private BufferedImage getFaceDownImage() {
// TODO: add download default images // TODO: add download default images
if (isPermanent()) { if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) { if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage(); return ImageCache.getMorphImage();
} else { } else {

View file

@ -93,7 +93,6 @@ public class CardView extends SimpleCardView {
protected boolean faceDown; protected boolean faceDown;
protected String alternateName; protected String alternateName;
protected String originalName;
protected boolean isSplitCard; protected boolean isSplitCard;
protected String leftSplitName; protected String leftSplitName;
@ -197,7 +196,6 @@ public class CardView extends SimpleCardView {
this.flipCard = cardView.flipCard; this.flipCard = cardView.flipCard;
this.faceDown = cardView.faceDown; this.faceDown = cardView.faceDown;
this.alternateName = cardView.alternateName; this.alternateName = cardView.alternateName;
this.originalName = cardView.originalName;
this.isSplitCard = cardView.isSplitCard; this.isSplitCard = cardView.isSplitCard;
this.leftSplitName = cardView.leftSplitName; this.leftSplitName = cardView.leftSplitName;
@ -508,13 +506,11 @@ public class CardView extends SimpleCardView {
if (secondSideCard != null) { if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard, game); this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName(); this.alternateName = secondCardFace.getName();
this.originalName = card.getName();
} }
this.flipCard = card.isFlipCard(); this.flipCard = card.isFlipCard();
if (card.isFlipCard() && card.getFlipCardName() != null) { if (card.isFlipCard() && card.getFlipCardName() != null) {
this.alternateName = card.getFlipCardName(); this.alternateName = card.getFlipCardName();
this.originalName = card.getName();
} }
if (card instanceof ModalDoubleFacesCard) { if (card instanceof ModalDoubleFacesCard) {
@ -522,7 +518,6 @@ public class CardView extends SimpleCardView {
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card; ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game); this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName(); this.alternateName = mdfCard.getRightHalfCard().getName();
this.originalName = card.getName();
} }
if (card instanceof Spell) { if (card instanceof Spell) {
@ -1006,24 +1001,10 @@ public class CardView extends SimpleCardView {
return alternateName; return alternateName;
} }
/**
* Stores the name of the original name, to provide it for a flipped or
* transformed or copying card
*
* @return
*/
public String getOriginalName() {
return originalName;
}
public void setAlternateName(String alternateName) { public void setAlternateName(String alternateName) {
this.alternateName = alternateName; this.alternateName = alternateName;
} }
public void setOriginalName(String originalName) {
this.originalName = originalName;
}
public String getLeftSplitName() { public String getLeftSplitName() {
return leftSplitName; return leftSplitName;
} }

View file

@ -71,12 +71,10 @@ public class PermanentView extends CardView {
if (original != null && !original.getName().equals(this.getName())) { if (original != null && !original.getName().equals(this.getName())) {
if (permanent.isCopy() && permanent.isFlipCard()) { if (permanent.isCopy() && permanent.isFlipCard()) {
this.alternateName = permanent.getFlipCardName(); this.alternateName = permanent.getFlipCardName();
this.originalName = this.getName();
} else { } else {
if (controlled // controller may always know if (controlled // controller may always know
|| (!morphed && !manifested)) { // others don't know for morph or transformed cards || (!morphed && !manifested)) { // others don't know for morph or transformed cards
this.alternateName = original.getName(); this.alternateName = original.getName();
this.originalName = this.getName();
} }
} }
} }

View file

@ -223,7 +223,7 @@ public class HumanPlayer extends PlayerImpl {
} }
} }
// game recived immidiate response on OTHER player concede -- need to process end game and continue to wait // game recived immediately response on OTHER player concede -- need to process end game and continue to wait
if (response.getResponseConcedeCheck()) { if (response.getResponseConcedeCheck()) {
((GameImpl) game).checkConcede(); ((GameImpl) game).checkConcede();
if (game.hasEnded()) { if (game.hasEnded()) {