1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-02 11:25:59 -09:00

* UI: improved and fixed possible targets highlighting:

* added blockers highlighting on declare blockers step;
   * fixed that blocker targets highlights all attackers instead real;
   * fixed wrong attackers draw in images render mode;
This commit is contained in:
Oleg Agafonov 2019-03-28 09:29:15 +04:00
parent c58b28f94f
commit 0e6dbb4eed
7 changed files with 167 additions and 132 deletions
Mage.Client/src/main/java
Mage.Common/src/main/java/mage
Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human

View file

@ -696,6 +696,13 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
List<UUID> possibleBlockers = new ArrayList<>();
if (options != null && options.containsKey(Constants.Option.POSSIBLE_BLOCKERS)) {
if (options.get(Constants.Option.POSSIBLE_BLOCKERS) instanceof List) {
possibleBlockers.addAll((List) options.get(Constants.Option.POSSIBLE_BLOCKERS));
}
}
for (PlayerView player : game.getPlayers()) { for (PlayerView player : game.getPlayers()) {
if (players.containsKey(player.getPlayerId())) { if (players.containsKey(player.getPlayerId())) {
if (!possibleAttackers.isEmpty()) { if (!possibleAttackers.isEmpty()) {
@ -705,6 +712,13 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
} }
if (!possibleBlockers.isEmpty()) {
for (UUID permanentId : possibleBlockers) {
if (player.getBattlefield().containsKey(permanentId)) {
player.getBattlefield().get(permanentId).setCanBlock(true);
}
}
}
players.get(player.getPlayerId()).update(player); players.get(player.getPlayerId()).update(player);
if (player.getPlayerId().equals(playerId)) { if (player.getPlayerId().equals(playerId)) {
skipButtons.updateFromPlayer(player); skipButtons.updateFromPlayer(player);

View file

@ -1,17 +1,5 @@
package org.mage.card.arcane; package org.mage.card.arcane;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.StringTokenizer;
import java.util.UUID;
import javax.swing.*;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.client.constants.Constants; import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
@ -25,6 +13,16 @@ import mage.view.CardView;
import mage.view.CounterView; import mage.view.CounterView;
import mage.view.PermanentView; import mage.view.PermanentView;
import mage.view.StackAbilityView; import mage.view.StackAbilityView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.StringTokenizer;
import java.util.UUID;
/** /**
* Class for drawing the mage card object by using a form based JComponent * Class for drawing the mage card object by using a form based JComponent
@ -117,8 +115,9 @@ public class CardPanelComponentImpl extends CardPanel {
final boolean isChoosable; final boolean isChoosable;
final boolean isPlayable; final boolean isPlayable;
final boolean canAttack; final boolean canAttack;
final boolean canBlock;
public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack) { public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack, boolean canBlock) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.cardWidth = cardWidth; this.cardWidth = cardWidth;
@ -130,6 +129,7 @@ public class CardPanelComponentImpl extends CardPanel {
this.isChoosable = isChoosable; this.isChoosable = isChoosable;
this.isPlayable = isPlayable; this.isPlayable = isPlayable;
this.canAttack = canAttack; this.canAttack = canAttack;
this.canBlock = canBlock;
} }
@Override @Override
@ -146,6 +146,7 @@ public class CardPanelComponentImpl extends CardPanel {
hash = 19 * hash + (this.isChoosable ? 1 : 0); hash = 19 * hash + (this.isChoosable ? 1 : 0);
hash = 19 * hash + (this.isPlayable ? 1 : 0); hash = 19 * hash + (this.isPlayable ? 1 : 0);
hash = 19 * hash + (this.canAttack ? 1 : 0); hash = 19 * hash + (this.canAttack ? 1 : 0);
hash = 19 * hash + (this.canBlock ? 1 : 0);
return hash; return hash;
} }
@ -194,7 +195,7 @@ public class CardPanelComponentImpl extends CardPanel {
if (this.canAttack != other.canAttack) { if (this.canAttack != other.canAttack) {
return false; return false;
} }
return true; return this.canBlock == other.canBlock;
} }
} }
@ -202,20 +203,20 @@ public class CardPanelComponentImpl extends CardPanel {
IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelComponentImpl::createImage)); IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelComponentImpl::createImage));
} }
static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage){ static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage) {
// cards without images show icons and text always // cards without images show icons and text always
// TODO: apply "card names on card" setting to icon too? // TODO: apply "card names on card" setting to icon too?
// TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200) // TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200)
return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage); return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage);
} }
private static class CardSizes{ private static class CardSizes {
Rectangle rectFull; Rectangle rectFull;
Rectangle rectSelection; Rectangle rectSelection;
Rectangle rectBorder; Rectangle rectBorder;
Rectangle rectCard; Rectangle rectCard;
CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight){ CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight) {
int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE); int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE);
int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE); int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE);
@ -406,7 +407,8 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.drawImage( g2d.drawImage(
IMAGE_CACHE.getOrThrow( IMAGE_CACHE.getOrThrow(
new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(), new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(),
hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack())), hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack(),
getGameCard().isCanBlock())),
0, 0, null); 0, 0, null);
g2d.dispose(); g2d.dispose();
} }
@ -442,6 +444,12 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection); g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection);
} }
// draw attack or block border (?inner part of selection?)
if (key.canAttack || key.canBlock) {
g2d.setColor(new Color(255, 50, 50, 230));
g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection);
}
// draw empty card with border // draw empty card with border
if (!key.hasImage) { if (!key.hasImage) {
// gray 1 px border // gray 1 px border
@ -452,12 +460,6 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder); g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
} }
// draw attack border (inner part of selection)
if (key.canAttack) {
g2d.setColor(new Color(0, 0, 255, 230));
g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
}
// draw real card by component (see imagePanel and other layout's items) // draw real card by component (see imagePanel and other layout's items)
//TODO:uncomment //TODO:uncomment
@ -521,7 +523,7 @@ public class CardPanelComponentImpl extends CardPanel {
StringTokenizer tok = new StringTokenizer(manaCost, " "); StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
tok.nextToken(); tok.nextToken();
if(width != 0) { if (width != 0) {
width += symbolMarginX; width += symbolMarginX;
} }
width += getSymbolWidth(); width += getSymbolWidth();

View file

@ -1,17 +1,7 @@
package org.mage.card.arcane; package org.mage.card.arcane;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.client.constants.Constants; import mage.client.constants.Constants;
import mage.constants.CardType; import mage.constants.CardType;
@ -21,6 +11,14 @@ import mage.view.CardView;
import mage.view.CounterView; import mage.view.CounterView;
import mage.view.PermanentView; import mage.view.PermanentView;
import mage.view.StackAbilityView; import mage.view.StackAbilityView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
public class CardPanelRenderImpl extends CardPanel { public class CardPanelRenderImpl extends CardPanel {
@ -107,9 +105,7 @@ public class CardPanelRenderImpl extends CardPanel {
// are the same for a and b // are the same for a and b
return false; return false;
} }
if (aa.getDamage() != bb.getDamage()) { return aa.getDamage() == bb.getDamage();
return false;
}
} }
return true; return true;
} }
@ -143,6 +139,7 @@ public class CardPanelRenderImpl extends CardPanel {
sb.append((char) (isChoosable ? 1 : 0)); sb.append((char) (isChoosable ? 1 : 0));
sb.append((char) (this.view.isPlayable() ? 1 : 0)); sb.append((char) (this.view.isPlayable() ? 1 : 0));
sb.append((char) (this.view.isCanAttack() ? 1 : 0)); sb.append((char) (this.view.isCanAttack() ? 1 : 0));
sb.append((char) (this.view.isCanBlock() ? 1 : 0));
sb.append((char) (this.view.isFaceDown() ? 1 : 0)); sb.append((char) (this.view.isFaceDown() ? 1 : 0));
sb.append((char) this.view.getFrameStyle().ordinal()); sb.append((char) this.view.getFrameStyle().ordinal());
if (this.view instanceof PermanentView) { if (this.view instanceof PermanentView) {
@ -266,8 +263,8 @@ public class CardPanelRenderImpl extends CardPanel {
// Try to get card image from cache based on our card characteristics // Try to get card image from cache based on our card characteristics
ImageKey key ImageKey key
= new ImageKey(getGameCard(), artImage, = new ImageKey(getGameCard(), artImage,
getCardWidth(), getCardHeight(), getCardWidth(), getCardHeight(),
isChoosable(), isSelected()); isChoosable(), isSelected());
try { try {
cardImage = IMAGE_CACHE.get(key, this::renderCard); cardImage = IMAGE_CACHE.get(key, this::renderCard);
} catch (ExecutionException e) { } catch (ExecutionException e) {

View file

@ -5,24 +5,6 @@
*/ */
package org.mage.card.arcane; package org.mage.card.arcane;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.*;
import mage.ObjectColor; import mage.ObjectColor;
import mage.cards.ArtRect; import mage.cards.ArtRect;
import mage.cards.FrameStyle; import mage.cards.FrameStyle;
@ -34,6 +16,22 @@ import mage.util.SubTypeList;
import mage.view.CardView; import mage.view.CardView;
import mage.view.PermanentView; import mage.view.PermanentView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.swing.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol; import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol;
@ -56,9 +54,10 @@ import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol;
render.draw(g, cardWidth, cardHeight); render.draw(g, cardWidth, cardHeight);
} }
*/ */
/** /**
* @author stravant@gmail.com * @author stravant@gmail.com
* * <p>
* Base rendering class for new border cards * Base rendering class for new border cards
*/ */
public class ModernCardRenderer extends CardRenderer { public class ModernCardRenderer extends CardRenderer {
@ -98,6 +97,7 @@ public class ModernCardRenderer extends CardRenderer {
} }
return new Font("Arial", Font.PLAIN, 1); return new Font("Arial", Font.PLAIN, 1);
} }
public static final Font BASE_BELEREN_FONT = loadFont("beleren-bold"); public static final Font BASE_BELEREN_FONT = loadFont("beleren-bold");
public static final Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white"); public static final Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white");
@ -275,7 +275,9 @@ public class ModernCardRenderer extends CardRenderer {
} else if (cardView.isPlayable()) { } else if (cardView.isPlayable()) {
borderColor = new Color(153, 102, 204, 200); borderColor = new Color(153, 102, 204, 200);
} else if (cardView.isCanAttack()) { } else if (cardView.isCanAttack()) {
borderColor = new Color(0, 0, 255, 230); borderColor = new Color(255, 50, 50, 230);
} else if (cardView.isCanBlock()) {
borderColor = new Color(255, 50, 50, 230);
} else { } else {
borderColor = Color.BLACK; borderColor = Color.BLACK;
} }
@ -659,7 +661,7 @@ public class ModernCardRenderer extends CardRenderer {
} }
public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
Color boxColor, Paint paint) { Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage; BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false; boolean hadToUseFullArt = false;
@ -707,8 +709,8 @@ public class ModernCardRenderer extends CardRenderer {
} }
public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
int topxdelta, int endydelta, int topxdelta, int endydelta,
Color boxColor, Paint paint) { Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage; BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false; boolean hadToUseFullArt = false;
if (faceArtImage == null) { if (faceArtImage == null) {
@ -764,10 +766,10 @@ public class ModernCardRenderer extends CardRenderer {
} }
public void drawUSTCurves(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2, public void drawUSTCurves(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
int topxdelta, int endydelta, int topxdelta, int endydelta,
Color boxColor, Paint paint) { Color boxColor, Paint paint) {
BufferedImage artToUse = artImage; BufferedImage artToUse = artImage;
int srcW = x2; int srcW = x2;
int srcH = y2; int srcH = y2;
if (artToUse != null) { if (artToUse != null) {
@ -999,23 +1001,23 @@ public class ModernCardRenderer extends CardRenderer {
Polygon symbol = new Polygon( Polygon symbol = new Polygon(
new int[]{ new int[]{
x + w / 2, x + w / 2,
(int) (x + w * 0.9), (int) (x + w * 0.9),
x + w, x + w,
(int) (x + w * 0.6), (int) (x + w * 0.6),
x + w / 2, x + w / 2,
(int) (x + w * 0.4), (int) (x + w * 0.4),
x, x,
(int) (x + w * 0.1),}, (int) (x + w * 0.1),},
new int[]{ new int[]{
y + h, y + h,
(int) (y + 0.8 * h), (int) (y + 0.8 * h),
y, y,
(int) (y - 0.2 * h), (int) (y - 0.2 * h),
y, y,
(int) (y - 0.2 * h), (int) (y - 0.2 * h),
y, y,
(int) (y + 0.8 * h),}, (int) (y + 0.8 * h),},
8); 8);
// Draw + stroke // Draw + stroke
@ -1124,7 +1126,7 @@ public class ModernCardRenderer extends CardRenderer {
drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
return; return;
} else // Big circle in the middle for Zendikar lands } else // Big circle in the middle for Zendikar lands
if (allRules.size() == 1) { if (allRules.size() == 1) {
// Size of mana symbol = 9/4 * h, 3/4h above line // Size of mana symbol = 9/4 * h, 3/4h above line
if (allRules.get(0) instanceof TextboxBasicManaRule) { if (allRules.get(0) instanceof TextboxBasicManaRule) {
drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
@ -1278,45 +1280,45 @@ public class ModernCardRenderer extends CardRenderer {
if (loyaltyRule.loyaltyChange < 0 || loyaltyRule.loyaltyChange == TextboxLoyaltyRule.MINUS_X) { if (loyaltyRule.loyaltyChange < 0 || loyaltyRule.loyaltyChange == TextboxLoyaltyRule.MINUS_X) {
symbol = new Polygon( symbol = new Polygon(
new int[]{ new int[]{
symbolX, symbolX,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX + symbolWidth / 2, symbolX + symbolWidth / 2,
symbolX,}, symbolX,},
new int[]{ new int[]{
symbolY, symbolY,
symbolY, symbolY,
symbolY + symbolHeight - 3, symbolY + symbolHeight - 3,
symbolY + symbolHeight + 3, symbolY + symbolHeight + 3,
symbolY + symbolHeight - 3,}, symbolY + symbolHeight - 3,},
5); 5);
} else if (loyaltyRule.loyaltyChange > 0) { } else if (loyaltyRule.loyaltyChange > 0) {
symbol = new Polygon( symbol = new Polygon(
new int[]{ new int[]{
symbolX, symbolX,
symbolX + symbolWidth / 2, symbolX + symbolWidth / 2,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX,}, symbolX,},
new int[]{ new int[]{
symbolY + 3, symbolY + 3,
symbolY - 3, symbolY - 3,
symbolY + 3, symbolY + 3,
symbolY + symbolHeight, symbolY + symbolHeight,
symbolY + symbolHeight,}, symbolY + symbolHeight,},
5); 5);
} else { } else {
symbol = new Polygon( symbol = new Polygon(
new int[]{ new int[]{
symbolX, symbolX,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX + symbolWidth, symbolX + symbolWidth,
symbolX,}, symbolX,},
new int[]{ new int[]{
symbolY, symbolY,
symbolY, symbolY,
symbolY + symbolHeight, symbolY + symbolHeight,
symbolY + symbolHeight,}, symbolY + symbolHeight,},
4); 4);
} }
g.setColor(new Color(0, 0, 0, 128)); g.setColor(new Color(0, 0, 0, 128));
@ -1610,13 +1612,13 @@ public class ModernCardRenderer extends CardRenderer {
Color[] translatedColors; Color[] translatedColors;
if (types.contains(CardType.LAND)) { if (types.contains(CardType.LAND)) {
translatedColors = new Color[]{ translatedColors = new Color[]{
getLandTextboxColor(twoColors.get(0)), getLandTextboxColor(twoColors.get(0)),
getLandTextboxColor(twoColors.get(1)) getLandTextboxColor(twoColors.get(1))
}; };
} else { } else {
translatedColors = new Color[]{ translatedColors = new Color[]{
getTextboxColor(twoColors.get(0)), getTextboxColor(twoColors.get(0)),
getTextboxColor(twoColors.get(1)) getTextboxColor(twoColors.get(1))
}; };
} }

View file

@ -1,8 +1,6 @@
package mage.constants; package mage.constants;
/** /**
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public final class Constants { public final class Constants {
@ -50,19 +48,14 @@ public final class Constants {
*/ */
public static final int PRIORITY_TIME_SEC = 1200; public static final int PRIORITY_TIME_SEC = 1200;
public enum Option { public enum Option {
; ;
public static final String POSSIBLE_ATTACKERS = "possibleAttackers"; public static final String POSSIBLE_ATTACKERS = "possibleAttackers";
public static final String POSSIBLE_BLOCKERS = "possibleBlockers";
public static final String SPECIAL_BUTTON = "specialButton"; public static final String SPECIAL_BUTTON = "specialButton";
// used to control automatic answers of optional effects // used to control automatic answers of optional effects
public static final String ORIGINAL_ID = "originalId"; public static final String ORIGINAL_ID = "originalId";
public static final String SECOND_MESSAGE = "secondMessage"; public static final String SECOND_MESSAGE = "secondMessage";
public static final String HINT_TEXT = "hintText"; public static final String HINT_TEXT = "hintText";
} }
} }

View file

@ -1,4 +1,3 @@
package mage.view; package mage.view;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
@ -109,6 +108,7 @@ public class CardView extends SimpleCardView {
protected boolean isChoosable; protected boolean isChoosable;
protected boolean selected; protected boolean selected;
protected boolean canAttack; protected boolean canAttack;
protected boolean canBlock;
protected boolean inViewerOnly; protected boolean inViewerOnly;
protected Card originalCard = null; protected Card originalCard = null;
@ -202,6 +202,7 @@ public class CardView extends SimpleCardView {
this.isChoosable = cardView.isChoosable; this.isChoosable = cardView.isChoosable;
this.selected = cardView.selected; this.selected = cardView.selected;
this.canAttack = cardView.canAttack; this.canAttack = cardView.canAttack;
this.canBlock = cardView.canBlock;
this.inViewerOnly = cardView.inViewerOnly; this.inViewerOnly = cardView.inViewerOnly;
this.originalCard = cardView.originalCard.copy(); this.originalCard = cardView.originalCard.copy();
} }
@ -286,7 +287,7 @@ public class CardView extends SimpleCardView {
this.power = Integer.toString(card.getPower().getValue()); this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue()); this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = card.getCardType(); this.cardTypes = card.getCardType();
this.faceDown = ((Permanent) card).isFaceDown(game); this.faceDown = card.isFaceDown(game);
} else { } else {
// this.hideInfo = true; // this.hideInfo = true;
return; return;
@ -296,9 +297,9 @@ public class CardView extends SimpleCardView {
SplitCard splitCard = null; SplitCard splitCard = null;
if (card.isSplitCard()) { if (card.isSplitCard()) {
splitCard = (SplitCard) card; splitCard = (SplitCard) card;
rotate = (((SplitCard) card).getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH; rotate = (card.getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH;
} else if (card instanceof Spell) { } else if (card instanceof Spell) {
switch (((Spell) card).getSpellAbility().getSpellAbilityType()) { switch (card.getSpellAbility().getSpellAbilityType()) {
case SPLIT_FUSED: case SPLIT_FUSED:
splitCard = (SplitCard) ((Spell) card).getCard(); splitCard = (SplitCard) ((Spell) card).getCard();
rotate = true; rotate = true;
@ -390,12 +391,12 @@ public class CardView extends SimpleCardView {
this.cardNumber = ((PermanentToken) card).getToken().getOriginalCardNumber(); this.cardNumber = ((PermanentToken) card).getToken().getOriginalCardNumber();
} else { } else {
// a created token // a created token
this.expansionSetCode = ((PermanentToken) card).getExpansionSetCode(); this.expansionSetCode = card.getExpansionSetCode();
this.tokenDescriptor = ((PermanentToken) card).getTokenDescriptor(); this.tokenDescriptor = card.getTokenDescriptor();
} }
// //
// set code und card number for token copies to get the image // set code und card number for token copies to get the image
this.rules = ((PermanentToken) card).getRules(game); this.rules = card.getRules(game);
this.type = ((PermanentToken) card).getToken().getTokenType(); this.type = ((PermanentToken) card).getToken().getTokenType();
} else { } else {
this.rarity = card.getRarity(); this.rarity = card.getRarity();
@ -995,6 +996,14 @@ public class CardView extends SimpleCardView {
this.canAttack = canAttack; this.canAttack = canAttack;
} }
public boolean isCanBlock() {
return canBlock;
}
public void setCanBlock(boolean canBlock) {
this.canBlock = canBlock;
}
public boolean isCreature() { public boolean isCreature() {
return cardTypes.contains(CardType.CREATURE); return cardTypes.contains(CardType.CREATURE);
} }

View file

@ -51,6 +51,7 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL; import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
@ -1432,7 +1433,12 @@ public class HumanPlayer extends PlayerImpl {
while (!abort) { while (!abort) {
prepareForResponse(game); prepareForResponse(game);
if (!isExecutingMacro()) { if (!isExecutingMacro()) {
game.fireSelectEvent(playerId, "Select blockers"); Map<String, Serializable> options = new HashMap<>();
List<UUID> possibleBlockers = game.getBattlefield().getActivePermanents(filter, playerId, game).stream()
.map(p -> p.getId())
.collect(Collectors.toList());
options.put(Constants.Option.POSSIBLE_BLOCKERS, (Serializable) possibleBlockers);
game.fireSelectEvent(playerId, "Select blockers", options);
} }
waitForResponse(game); waitForResponse(game);
if (response.getBoolean() != null) { if (response.getBoolean() != null) {
@ -1504,9 +1510,21 @@ public class HumanPlayer extends PlayerImpl {
TargetAttackingCreature target = new TargetAttackingCreature(); TargetAttackingCreature target = new TargetAttackingCreature();
prepareForResponse(game); prepareForResponse(game);
if (!isExecutingMacro()) { if (!isExecutingMacro()) {
// possible attackers to block
Set<UUID> attackers = target.possibleTargets(null, playerId, game);
Permanent blocker = game.getPermanent(blockerId);
Set<UUID> possibleTargets = new HashSet<>();
for (UUID attackerId : attackers) {
CombatGroup group = game.getCombat().findGroup(attackerId);
if (group != null && blocker != null && group.canBlock(blocker, game)) {
possibleTargets.add(attackerId);
}
}
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)), game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
target.possibleTargets(null, playerId, game), false, getOptions(target, null)); possibleTargets, false, getOptions(target, null));
} }
waitForResponse(game); waitForResponse(game);
if (response.getBoolean() != null) { if (response.getBoolean() != null) {
// do nothing // do nothing