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
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(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", "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

View file

@ -499,7 +499,7 @@ public class MageActionCallback implements ActionCallback {
}
popupTextWindowOpen = true;
Image image = cardPanel.getImage();
displayCardInfo(cardPanel, image, bigCard);
displayCardInfo(cardPanel.getOriginal(), image, bigCard);
}
}
} else {
@ -671,7 +671,9 @@ public class MageActionCallback implements ActionCallback {
popupContainer.setLocation(location);
popupContainer.setVisible(true);
// popup hint mode
Image image = null;
CardView displayCard = cardPanel.getOriginal();
switch (enlargeMode) {
case COPY:
if (cardView instanceof PermanentView) {
@ -687,6 +689,7 @@ public class MageActionCallback implements ActionCallback {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal());
} else {
image = ImageCache.getImageOriginalAlternateName(cardView);
displayCard = displayCard.getSecondCardFace();
}
}
break;
@ -697,7 +700,7 @@ public class MageActionCallback implements ActionCallback {
image = cardPanel.getImage();
}
// shows the card in the popup Container
displayCardInfo(cardPanel, image, (BigCard) cardPreviewPane);
displayCardInfo(displayCard, image, (BigCard) cardPreviewPane);
} else {
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) {
// 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 (mageCard.getOriginal().isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) {
if (card.isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) {
bigCard.showTextComponent();
} else {
bigCard.hideTextComponent();
}
} else {
JXPanel panel = GuiDisplayUtil.getDescription(mageCard.getOriginal(), bigCard.getWidth(), bigCard.getHeight());
JXPanel panel = GuiDisplayUtil.getDescription(card, bigCard.getWidth(), bigCard.getHeight());
panel.setVisible(true);
bigCard.hideTextComponent();
bigCard.addJXPanel(mageCard.getOriginal().getId(), panel);
bigCard.addJXPanel(card.getId(), panel);
}
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 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;
// if null then gameCard contains main card
// if not null then gameCard contains second side
private CardView temporary;
private double tappedAngle = 0;
private double flippedAngle = 0;
@ -98,7 +96,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
public double transformAngle = 1;
private boolean transformed;
private boolean guiTransformed;
private boolean animationInProgress = false;
private Container cardContainer;
@ -115,6 +113,8 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Store away params
this.setGameCard(newGameCard);
this.setGameCardSides(newGameCard);
this.callback = callback;
this.gameId = gameId;
this.needFullPermanentRender = needFullPermanentRender;
@ -153,16 +153,24 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Create the day night button
dayNightButton = new JButton("");
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();
dayNightButton.setIcon(new ImageIcon(day));
dayNightButton.addActionListener(e -> {
// if card is being rotated, ignore action performed
// if card is tapped, no visual transforming is possible (implementation limitation)
// if card is permanent, it will be rotated by Mage, so manual rotate should be possible
if (animationInProgress || isTapped() || isPermanent) {
// if card is rotating then ignore it
if (animationInProgress) {
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);
});
@ -470,7 +478,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isTapped() {
if (isPermanent) {
if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isTapped();
}
return false;
@ -478,7 +486,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isFlipped() {
if (isPermanent) {
if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isFlipped();
}
return false;
@ -486,15 +494,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isTransformed() {
if (isPermanent) {
if (getGameCard().isTransformed()) {
return !this.transformed;
} else {
return this.transformed;
}
} else {
return this.transformed;
}
return this.guiTransformed;
}
@Override
@ -521,7 +521,9 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
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)
// if card temporary transformed before (by icon click) then do not update full data (as example, after selection changed)
this.isChoosable = card.isChoosable();
@ -542,7 +544,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
AudioManager.playTapPermanent();
}
boolean needsTranforming = isTransformed() != card.isTransformed();
if (needsTranforming) {
if (needsTranforming && !animationInProgress) {
Animation.transformCard(this);
}
}
@ -558,6 +560,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Set the new card
this.setGameCard(card);
this.setGameCardSides(card);
// Update tooltip text
String cardType = getType(card);
@ -571,13 +574,13 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Update transform circle
if (card.canTransform()) {
BufferedImage transformIcon;
if (isTransformed() || card.isTransformed()) {
if (isTransformed() || card.isTransformed()) { // wtf
transformIcon = ImageManagerImpl.instance.getNightImage();
} else {
transformIcon = ImageManagerImpl.instance.getDayImage();
}
if (dayNightButton != null) {
dayNightButton.setVisible(!isPermanent);
dayNightButton.setVisible(true); // show T button for any cards and permanents
dayNightButton.setIcon(new ImageIcon(transformIcon));
}
}
@ -585,13 +588,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public CardView getOriginal() {
if (this.temporary == null) {
// current side: main, return: main
return this.getGameCard();
} else {
// current side: second, return: main
return this.temporary;
}
return this.cardSideMain;
}
@Override
@ -827,16 +824,14 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
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) {
return (PermanentView) this.getGameCard();
return (PermanentView) this.cardSideMain;
}
throw new IllegalStateException("Is not permanent.");
}
public void setTransformed(boolean transformed) {
this.transformed = transformed;
}
private void copySelections(CardView source, CardView dest) {
if (source != null && dest != null) {
dest.setSelected(source.isSelected());
@ -847,40 +842,31 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public void toggleTransformed() {
this.transformed = !this.transformed;
if (transformed) {
// show night card
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage night = ImageManagerImpl.instance.getNightImage();
dayNightButton.setIcon(new ImageIcon(night));
}
if (this.getGameCard().getSecondCardFace() == null) {
// VIEW mode (user can change card side at any time by n/d button)
this.guiTransformed = !this.guiTransformed;
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage image = this.guiTransformed ? ImageManagerImpl.instance.getNightImage() : ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(image));
}
// switch card data
if (this.guiTransformed) {
// main side -> alternative side
if (this.cardSideOther == null) {
logger.error("no second side for card to transform!");
return;
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
copySelections(this.getGameCard(), this.getGameCard().getSecondCardFace());
this.setTemporary(this.getGameCard());
update(this.getGameCard().getSecondCardFace());
}
copySelections(this.cardSideMain, this.cardSideOther);
update(this.cardSideOther);
this.getGameCard().setAlternateName(this.cardSideMain.getName());
} else {
// show day card
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage day = ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(day));
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
copySelections(this.getGameCard().getSecondCardFace(), this.getGameCard());
update(this.getTemporary());
this.setTemporary(null);
}
// alternative side -> main side
copySelections(this.cardSideOther, this.cardSideMain);
update(this.cardSideMain);
this.getGameCard().setAlternateName(this.cardSideOther.getName());
}
// switch card names for render
String temp = this.getGameCard().getAlternateName();
this.getGameCard().setAlternateName(this.getGameCard().getOriginalName());
this.getGameCard().setOriginalName(temp);
updateArtImage();
}
@ -939,6 +925,31 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
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() {
return updateCard;
}
@ -947,14 +958,6 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
this.updateCard = updateCard;
}
public CardView getTemporary() {
return temporary;
}
public void setTemporary(CardView temporary) {
this.temporary = temporary;
}
public double getTappedAngle() {
return tappedAngle;
}
@ -986,6 +989,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
needLocation.getCardHeight()
);
Rectangle animatedRect = MageLayer.animateCoords(this, normalRect);
return animatedRect.contains(x, y);
}

View file

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

View file

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

View file

@ -93,7 +93,6 @@ public class CardView extends SimpleCardView {
protected boolean faceDown;
protected String alternateName;
protected String originalName;
protected boolean isSplitCard;
protected String leftSplitName;
@ -197,7 +196,6 @@ public class CardView extends SimpleCardView {
this.flipCard = cardView.flipCard;
this.faceDown = cardView.faceDown;
this.alternateName = cardView.alternateName;
this.originalName = cardView.originalName;
this.isSplitCard = cardView.isSplitCard;
this.leftSplitName = cardView.leftSplitName;
@ -508,13 +506,11 @@ public class CardView extends SimpleCardView {
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName();
this.originalName = card.getName();
}
this.flipCard = card.isFlipCard();
if (card.isFlipCard() && card.getFlipCardName() != null) {
this.alternateName = card.getFlipCardName();
this.originalName = card.getName();
}
if (card instanceof ModalDoubleFacesCard) {
@ -522,7 +518,6 @@ public class CardView extends SimpleCardView {
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName();
this.originalName = card.getName();
}
if (card instanceof Spell) {
@ -1006,24 +1001,10 @@ public class CardView extends SimpleCardView {
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) {
this.alternateName = alternateName;
}
public void setOriginalName(String originalName) {
this.originalName = originalName;
}
public String getLeftSplitName() {
return leftSplitName;
}

View file

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