diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java index d20580a53e7..3ca4c7feac9 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java @@ -5,6 +5,7 @@ */ package org.mage.card.arcane; +import mage.cards.ArtRect; import mage.client.dialog.PreferencesDialog; import mage.constants.AbilityType; import mage.constants.CardType; @@ -124,7 +125,12 @@ public abstract class CardRenderer { this.cardView = card; this.isTransformed = isTransformed; - parseRules(card.getRules(), textboxKeywords, textboxRules); + if (card.getArtRect() == ArtRect.SPLIT_FUSED) { + parseRules(card.getLeftSplitRules(), textboxKeywords, textboxRules); + parseRules(card.getRightSplitRules(), textboxKeywords, textboxRules); + } else { + parseRules(card.getRules(), textboxKeywords, textboxRules); + } } protected void parseRules(List stringRules, ArrayList keywords, ArrayList rules) { @@ -261,7 +267,7 @@ public abstract class CardRenderer { } } - protected void drawArtIntoRect(Graphics2D g, int x, int y, int w, int h, Rectangle2D artRect, boolean noAspectAdjust) { + protected void drawArtIntoRect(Graphics2D g, int x, int y, int w, int h, Rectangle2D artRect, boolean shouldPreserveAspect) { // Perform a process to make sure that the art is scaled uniformly to fill the frame, cutting // off the minimum amount necessary to make it completely fill the frame without "squashing" it. double fullCardImgWidth = artImage.getWidth(); @@ -271,7 +277,7 @@ public abstract class CardRenderer { double targetWidth = w; double targetHeight = h; double targetAspect = targetWidth / targetHeight; - if (noAspectAdjust) { + if (!shouldPreserveAspect) { // No adjustment to art } else if (targetAspect * artHeight < artWidth) { // Trim off some width @@ -285,17 +291,10 @@ public abstract class CardRenderer { = artImage.getSubimage( (int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight), (int) artWidth, (int) artHeight); - if (noAspectAdjust) { - g.drawImage(subImg, - borderWidth, borderWidth, - cardWidth - 2 * borderWidth, cardHeight - 2 * borderWidth, - null); - } else { - g.drawImage(subImg, - x, y, - (int) targetWidth, (int) targetHeight, - null); - } + g.drawImage(subImg, + x, y, + (int) targetWidth, (int) targetHeight, + null); } catch (RasterFormatException e) { // At very small card sizes we may encounter a problem with rounding error making the rect not fit } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java index f738321cb36..39ba0670aab 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java @@ -1,5 +1,6 @@ package org.mage.card.arcane; +import mage.cards.ArtRect; import mage.view.CardView; /** @@ -11,7 +12,9 @@ public class CardRendererFactory { } public CardRenderer create(CardView card, boolean isTransformed) { - if (card.isSplitCard()) { + if (card.isSplitCard() && card.getArtRect() != ArtRect.SPLIT_FUSED) { + // Split fused cards still render with the normal frame, showing all abilities + // from both halves in one frame. return new ModernSplitCardRenderer(card, isTransformed); } else { return new ModernCardRenderer(card, isTransformed); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index da54a60e780..55ac6c502a8 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -6,9 +6,12 @@ package org.mage.card.arcane; import mage.ObjectColor; +import mage.cards.ArtRect; import mage.cards.FrameStyle; import mage.client.dialog.PreferencesDialog; import mage.constants.CardType; +import mage.constants.MageObjectType; +import mage.constants.SpellAbilityType; import mage.view.CardView; import mage.view.PermanentView; import org.apache.log4j.Logger; @@ -311,7 +314,7 @@ public class ModernCardRenderer extends CardRenderer { } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); } else { - rect = new Rectangle2D.Float(.079f, .11f, .84f, .42f); + rect = ArtRect.NORMAL.rect; } return rect; } @@ -346,10 +349,42 @@ public class ModernCardRenderer extends CardRenderer { @Override protected void drawArt(Graphics2D g) { if (artImage != null && !cardView.isFaceDown()) { + // Invention rendering, art fills the entire frame + if (useInventionFrame()) { + drawArtIntoRect(g, + borderWidth, borderWidth, + cardWidth - 2*borderWidth, cardHeight - 2*borderWidth, + getArtRect(), false); + } + + boolean shouldPreserveAspect = true; + Rectangle2D sourceRect = getArtRect(); + + if (cardView.getMageObjectType() == MageObjectType.SPELL) { + ArtRect rect = cardView.getArtRect(); + if (rect == ArtRect.SPLIT_FUSED) { + // Special handling for fused, draw the art from both halves stacked on top of one and other + // each filling half of the art rect + drawArtIntoRect(g, + totalContentInset + 1, totalContentInset + boxHeight, + contentWidth - 2, (typeLineY - totalContentInset - boxHeight)/2, + ArtRect.SPLIT_LEFT.rect, useInventionFrame()); + drawArtIntoRect(g, + totalContentInset + 1, totalContentInset + boxHeight + (typeLineY - totalContentInset - boxHeight)/2, + contentWidth - 2, (typeLineY - totalContentInset - boxHeight)/2, + ArtRect.SPLIT_RIGHT.rect, useInventionFrame()); + return; + } else if (rect != ArtRect.NORMAL) { + sourceRect = rect.rect; + shouldPreserveAspect = false; + } + } + + // Normal drawing of art from a source part of the card frame into the rect drawArtIntoRect(g, totalContentInset + 1, totalContentInset + boxHeight, contentWidth - 2, typeLineY - totalContentInset - boxHeight, - getArtRect(), useInventionFrame()); + sourceRect, shouldPreserveAspect); } } @@ -443,7 +478,7 @@ public class ModernCardRenderer extends CardRenderer { int nameOffset = drawTransformationCircle(g, borderPaint); // Draw the name line - drawNameLine(g, cardView.getName(), manaCostString, + drawNameLine(g, cardView.getDisplayName(), manaCostString, totalContentInset + nameOffset, totalContentInset, contentWidth - nameOffset, boxHeight); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java index 26ed83ca988..98d01254c6d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java @@ -2,6 +2,7 @@ package org.mage.card.arcane; import mage.ObjectColor; import mage.abilities.costs.mana.ManaCosts; +import mage.cards.ArtRect; import mage.constants.CardType; import mage.view.CardView; @@ -19,6 +20,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { int x, y, w, h, cw, ch; String name; + String typeLineString; String manaCostString; ObjectColor color; ArrayList rules = new ArrayList<>(); @@ -51,6 +53,9 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { parseRules(view.getRightSplitRules(), rightHalf.keywords, rightHalf.rules); parseRules(view.getLeftSplitRules(), leftHalf.keywords, leftHalf.rules); + rightHalf.typeLineString = cardView.getRightSplitTypeLine(); + leftHalf.typeLineString = cardView.getLeftSplitTypeLine(); + rightHalf.name = cardView.getRightSplitName(); leftHalf.name = cardView.getLeftSplitName(); @@ -186,26 +191,28 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { protected void drawArt(Graphics2D g) { if (artImage != null && !cardView.isFaceDown()) { if (isAftermath()) { - Rectangle2D topRect = new Rectangle2D.Double(0.075, 0.113, 0.832, 0.227); + Rectangle2D topRect = ArtRect.AFTERMATH_TOP.rect; int topLineY = (int) (leftHalf.ch * TYPE_LINE_Y_FRAC); drawArtIntoRect(g, leftHalf.x, leftHalf.y + boxHeight, leftHalf.cw, topLineY - boxHeight, topRect, false); - Rectangle2D bottomRect = new Rectangle2D.Double(0.546, 0.562, 0.272, 0.346); + Rectangle2D bottomRect = ArtRect.AFTERMATH_BOTTOM.rect; int bottomLineY = (rightHalf.ch - boxHeight) / 2; drawArtIntoRect(g, rightHalf.x + rightHalf.w - bottomLineY, rightHalf.y, bottomLineY - boxHeight, rightHalf.h, bottomRect, false); } else { - Rectangle2D topRect = new Rectangle2D.Double(0.152, 0.058, 0.386, 0.400); + // NOTE: Art rects are reversed here, that is on purpose because we swap the left / right half + // of split cards for rendering for consistency between aftermath and normal split + Rectangle2D topRect = ArtRect.SPLIT_RIGHT.rect; int topLineY = (int) (leftHalf.ch * TYPE_LINE_Y_FRAC); drawArtIntoRect(g, leftHalf.x + boxHeight, leftHalf.y, topLineY - boxHeight, leftHalf.h, topRect, false); - Rectangle2D bottomRect = new Rectangle2D.Double(0.152, 0.539, 0.386, 0.400); + Rectangle2D bottomRect = ArtRect.SPLIT_LEFT.rect; int bottomLineY = (int) (rightHalf.ch * TYPE_LINE_Y_FRAC); drawArtIntoRect(g, rightHalf.x + boxHeight, rightHalf.y, bottomLineY - boxHeight, rightHalf.h, @@ -252,7 +259,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { half.cw, boxHeight); // Draw the type line - drawTypeLine(g, getCardTypeLine(), + drawTypeLine(g, half.typeLineString, 0, typeLineY, half.cw, boxHeight - 4); diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 757c08567fe..b611d8ae766 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -29,12 +29,13 @@ package mage.view; import mage.MageObject; import mage.ObjectColor; +import mage.abilities.Abilities; +import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.costs.mana.ManaCosts; -import mage.cards.Card; -import mage.cards.FrameStyle; -import mage.cards.SplitCard; +import mage.abilities.keyword.AftermathAbility; +import mage.cards.*; import mage.constants.*; import mage.counters.Counter; import mage.counters.CounterType; @@ -99,9 +100,13 @@ public class CardView extends SimpleCardView { protected String leftSplitName; protected ManaCosts leftSplitCosts; protected List leftSplitRules; + protected String leftSplitTypeLine; protected String rightSplitName; protected ManaCosts rightSplitCosts; protected List rightSplitRules; + protected String rightSplitTypeLine; + + protected ArtRect artRect = ArtRect.NORMAL; protected List targets; @@ -179,14 +184,17 @@ public class CardView extends SimpleCardView { this.alternateName = cardView.alternateName; this.originalName = cardView.originalName; + this.artRect = cardView.artRect; this.isSplitCard = cardView.isSplitCard; this.leftSplitName = cardView.leftSplitName; this.leftSplitCosts = cardView.leftSplitCosts; this.leftSplitRules = null; + this.leftSplitTypeLine = cardView.leftSplitTypeLine; this.rightSplitName = cardView.rightSplitName; this.rightSplitCosts = cardView.rightSplitCosts; this.rightSplitRules = null; + this.rightSplitTypeLine = cardView.rightSplitTypeLine; this.targets = null; @@ -219,6 +227,23 @@ public class CardView extends SimpleCardView { this(card, game, controlled, false, false); } + private static String getCardTypeLine(Game game, Card card) { + StringBuilder sbType = new StringBuilder(); + for (SuperType superType : card.getSuperType()) { + sbType.append(superType).append(' '); + } + for (CardType cardType : card.getCardType()) { + sbType.append(cardType.toString()).append(' '); + } + if (!card.getSubtype(game).isEmpty()) { + sbType.append("- "); + for (String subType : card.getSubtype(game)) { + sbType.append(subType).append(' '); + } + } + return sbType.toString(); + } + /** * * @param card @@ -298,9 +323,11 @@ public class CardView extends SimpleCardView { leftSplitName = splitCard.getLeftHalfCard().getName(); leftSplitCosts = splitCard.getLeftHalfCard().getManaCost(); leftSplitRules = splitCard.getLeftHalfCard().getRules(game); + leftSplitTypeLine = getCardTypeLine(game, splitCard.getLeftHalfCard()); rightSplitName = splitCard.getRightHalfCard().getName(); rightSplitCosts = splitCard.getRightHalfCard().getManaCost(); rightSplitRules = splitCard.getRightHalfCard().getRules(game); + rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard()); } this.name = card.getImageName(); @@ -397,6 +424,35 @@ public class CardView extends SimpleCardView { } } } + + // Determine what part of the art to slice out for spells on the stack which originate + // from a split, fuse, or aftermath split card. + SpellAbilityType ty = spell.getSpellAbility().getSpellAbilityType(); + if (ty == SpellAbilityType.SPLIT_RIGHT || ty == SpellAbilityType.SPLIT_LEFT || ty == SpellAbilityType.SPLIT_FUSED) { + // Needs a special art rect + if (ty == SpellAbilityType.SPLIT_FUSED) { + artRect = ArtRect.SPLIT_FUSED; + } else { + if (spell.getCard() != null) { + SplitCard wholeCard = ((SplitCardHalf)spell.getCard()).getParentCard(); + Abilities aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(); + if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) { + if (ty == SpellAbilityType.SPLIT_RIGHT) { + artRect = ArtRect.AFTERMATH_BOTTOM; + } else { + artRect = ArtRect.AFTERMATH_TOP; + } + } else { + if (ty == SpellAbilityType.SPLIT_RIGHT) { + artRect = ArtRect.SPLIT_RIGHT; + } else { + artRect = ArtRect.SPLIT_LEFT; + } + } + } + } + } + // show for modal spell, which mode was choosen if (spell.getSpellAbility().isModal()) { for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) { @@ -781,6 +837,10 @@ public class CardView extends SimpleCardView { return leftSplitRules; } + public String getLeftSplitTypeLine() { + return leftSplitTypeLine; + } + public String getRightSplitName() { return rightSplitName; } @@ -793,6 +853,14 @@ public class CardView extends SimpleCardView { return rightSplitRules; } + public String getRightSplitTypeLine() { + return rightSplitTypeLine; + } + + public ArtRect getArtRect() { + return artRect; + } + public CardView getSecondCardFace() { return this.secondCardFace; } diff --git a/Mage/src/main/java/mage/cards/ArtRect.java b/Mage/src/main/java/mage/cards/ArtRect.java new file mode 100644 index 00000000000..d4453c29ccc --- /dev/null +++ b/Mage/src/main/java/mage/cards/ArtRect.java @@ -0,0 +1,21 @@ +package mage.cards; + +import java.awt.geom.Rectangle2D; + +/** + * Created by stravant@gmail.com on 2017-04-04. + */ +public enum ArtRect { + NORMAL(new Rectangle2D.Double(.079f, .11f, .84f, .42f)), + AFTERMATH_TOP(new Rectangle2D.Double(0.075, 0.113, 0.832, 0.227)), + AFTERMATH_BOTTOM(new Rectangle2D.Double(0.546, 0.562, 0.272, 0.346)), + SPLIT_LEFT(new Rectangle2D.Double(0.152, 0.539, 0.386, 0.400)), + SPLIT_RIGHT(new Rectangle2D.Double(0.152, 0.058, 0.386, 0.400)), + SPLIT_FUSED(null); + + public final Rectangle2D rect; + + ArtRect(Rectangle2D.Double rect) { + this.rect = rect; + } +}