mirror of
https://github.com/correl/mage.git
synced 2025-02-26 19:12:27 +00:00
* AI: fixed that computer can't target cards on battlefield if it contains tokens;
This commit is contained in:
parent
de07960ee5
commit
50195e8f35
4 changed files with 121 additions and 78 deletions
|
@ -18,41 +18,40 @@ import mage.view.PermanentView;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.RasterFormatException;
|
import java.awt.image.RasterFormatException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author stravant@gmail.com
|
* @author stravant@gmail.com
|
||||||
*
|
* <p>
|
||||||
* Common base class for card renderers for each card frame / card type.
|
* Common base class for card renderers for each card frame / card type.
|
||||||
*
|
* <p>
|
||||||
* Follows the template method pattern to implement a new renderer, implement
|
* Follows the template method pattern to implement a new renderer, implement
|
||||||
* the following methods (they are called in the following order):
|
* the following methods (they are called in the following order):
|
||||||
*
|
* <p>
|
||||||
* * drawBorder() Draws the outermost border of the card, white border or black
|
* * drawBorder() Draws the outermost border of the card, white border or black
|
||||||
* border
|
* border
|
||||||
*
|
* <p>
|
||||||
* * drawBackground() Draws the background texture / color of the card
|
* * drawBackground() Draws the background texture / color of the card
|
||||||
*
|
* <p>
|
||||||
* * drawArt() Draws the card's art
|
* * drawArt() Draws the card's art
|
||||||
*
|
* <p>
|
||||||
* * drawFrame() Draws the card frame (over the art and background)
|
* * drawFrame() Draws the card frame (over the art and background)
|
||||||
*
|
* <p>
|
||||||
* * drawOverlays() Draws summoning sickness and possible other overlays
|
* * drawOverlays() Draws summoning sickness and possible other overlays
|
||||||
*
|
* <p>
|
||||||
* * drawCounters() Draws counters on the card, such as +1/+1 and -1/-1
|
* * drawCounters() Draws counters on the card, such as +1/+1 and -1/-1
|
||||||
* counters
|
* counters
|
||||||
*
|
* <p>
|
||||||
* Predefined methods that the implementations can use:
|
* Predefined methods that the implementations can use:
|
||||||
*
|
* <p>
|
||||||
* * drawRules(font, bounding box)
|
* * drawRules(font, bounding box)
|
||||||
*
|
* <p>
|
||||||
* * drawNameLine(font, bounding box)
|
* * drawNameLine(font, bounding box)
|
||||||
*
|
* <p>
|
||||||
* * drawTypeLine(font, bounding box)
|
* * drawTypeLine(font, bounding box)
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class CardRenderer {
|
public abstract class CardRenderer {
|
||||||
|
|
||||||
|
@ -74,24 +73,24 @@ public abstract class CardRenderer {
|
||||||
// Common layout metrics between all cards
|
// Common layout metrics between all cards
|
||||||
// Polygons for counters
|
// Polygons for counters
|
||||||
private static final Polygon PLUS_COUNTER_POLY = new Polygon(new int[]{
|
private static final Polygon PLUS_COUNTER_POLY = new Polygon(new int[]{
|
||||||
0, 5, 10, 10, 5, 0
|
0, 5, 10, 10, 5, 0
|
||||||
}, new int[]{
|
}, new int[]{
|
||||||
3, 0, 3, 10, 9, 10
|
3, 0, 3, 10, 9, 10
|
||||||
}, 6);
|
}, 6);
|
||||||
private static final Polygon MINUS_COUNTER_POLY = new Polygon(new int[]{
|
private static final Polygon MINUS_COUNTER_POLY = new Polygon(new int[]{
|
||||||
0, 5, 10, 10, 5, 0
|
0, 5, 10, 10, 5, 0
|
||||||
}, new int[]{
|
}, new int[]{
|
||||||
0, 1, 0, 7, 10, 7
|
0, 1, 0, 7, 10, 7
|
||||||
}, 6);
|
}, 6);
|
||||||
private static final Polygon TIME_COUNTER_POLY = new Polygon(new int[]{
|
private static final Polygon TIME_COUNTER_POLY = new Polygon(new int[]{
|
||||||
0, 10, 8, 10, 0, 2
|
0, 10, 8, 10, 0, 2
|
||||||
}, new int[]{
|
}, new int[]{
|
||||||
0, 0, 5, 10, 10, 5
|
0, 0, 5, 10, 10, 5
|
||||||
}, 6);
|
}, 6);
|
||||||
private static final Polygon OTHER_COUNTER_POLY = new Polygon(new int[]{
|
private static final Polygon OTHER_COUNTER_POLY = new Polygon(new int[]{
|
||||||
1, 9, 9, 1
|
1, 9, 9, 1
|
||||||
}, new int[]{
|
}, new int[]{
|
||||||
1, 1, 9, 9
|
1, 1, 9, 9
|
||||||
}, 4);
|
}, 4);
|
||||||
|
|
||||||
// Paint for a card back
|
// Paint for a card back
|
||||||
|
@ -253,11 +252,11 @@ public abstract class CardRenderer {
|
||||||
int x2 = (int) (0.8 * cardWidth);
|
int x2 = (int) (0.8 * cardWidth);
|
||||||
int y1 = (int) (0.2 * cardHeight);
|
int y1 = (int) (0.2 * cardHeight);
|
||||||
int y2 = (int) (0.8 * cardHeight);
|
int y2 = (int) (0.8 * cardHeight);
|
||||||
int xPoints[] = {
|
int[] xPoints = {
|
||||||
x1, x2, x1, x2
|
x1, x2, x1, x2
|
||||||
};
|
};
|
||||||
int yPoints[] = {
|
int[] yPoints = {
|
||||||
y1, y1, y2, y2
|
y1, y1, y2, y2
|
||||||
};
|
};
|
||||||
g.setColor(new Color(255, 255, 255, 200));
|
g.setColor(new Color(255, 255, 255, 200));
|
||||||
g.setStroke(new BasicStroke(7));
|
g.setStroke(new BasicStroke(7));
|
||||||
|
@ -267,10 +266,10 @@ public abstract class CardRenderer {
|
||||||
g.drawPolygon(xPoints, yPoints, 4);
|
g.drawPolygon(xPoints, yPoints, 4);
|
||||||
g.setStroke(new BasicStroke(1));
|
g.setStroke(new BasicStroke(1));
|
||||||
int[] xPoints2 = {
|
int[] xPoints2 = {
|
||||||
x1, x2, cardWidth / 2
|
x1, x2, cardWidth / 2
|
||||||
};
|
};
|
||||||
int[] yPoints2 = {
|
int[] yPoints2 = {
|
||||||
y1, y1, cardHeight / 2
|
y1, y1, cardHeight / 2
|
||||||
};
|
};
|
||||||
g.setColor(new Color(0, 0, 0, 100));
|
g.setColor(new Color(0, 0, 0, 100));
|
||||||
g.fillPolygon(xPoints2, yPoints2, 3);
|
g.fillPolygon(xPoints2, yPoints2, 3);
|
||||||
|
@ -300,8 +299,8 @@ public abstract class CardRenderer {
|
||||||
try {
|
try {
|
||||||
BufferedImage subImg
|
BufferedImage subImg
|
||||||
= artImage.getSubimage(
|
= artImage.getSubimage(
|
||||||
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
|
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
|
||||||
(int) artWidth, (int) artHeight);
|
(int) artWidth, (int) artHeight);
|
||||||
g.drawImage(subImg,
|
g.drawImage(subImg,
|
||||||
x, y,
|
x, y,
|
||||||
(int) targetWidth, (int) targetHeight,
|
(int) targetWidth, (int) targetHeight,
|
||||||
|
@ -438,20 +437,25 @@ public abstract class CardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getRarityColor() {
|
private Color getRarityColor() {
|
||||||
switch (cardView.getRarity()) {
|
if (cardView.getRarity() != null) {
|
||||||
case RARE:
|
switch (cardView.getRarity()) {
|
||||||
return new Color(255, 191, 0);
|
case RARE:
|
||||||
case UNCOMMON:
|
return new Color(255, 191, 0);
|
||||||
return new Color(192, 192, 192);
|
case UNCOMMON:
|
||||||
case MYTHIC:
|
return new Color(192, 192, 192);
|
||||||
return new Color(213, 51, 11);
|
case MYTHIC:
|
||||||
case SPECIAL:
|
return new Color(213, 51, 11);
|
||||||
return new Color(204, 0, 255);
|
case SPECIAL:
|
||||||
case BONUS:
|
return new Color(204, 0, 255);
|
||||||
return new Color(129, 228, 228);
|
case BONUS:
|
||||||
case COMMON:
|
return new Color(129, 228, 228);
|
||||||
default:
|
case COMMON:
|
||||||
return Color.black;
|
default:
|
||||||
|
return Color.black;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// tokens
|
||||||
|
return Color.black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,4 +70,40 @@ public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase {
|
||||||
assertHandCount(playerB, "Pillarfield Ox", 1);
|
assertHandCount(playerB, "Pillarfield Ox", 1);
|
||||||
assertExileCount(playerB, 0);
|
assertExileCount(playerB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAngelOfSerenityTargets() {
|
||||||
|
// test NPE error while AI targeting battlefield with tokens
|
||||||
|
|
||||||
|
// Flying
|
||||||
|
// When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards.
|
||||||
|
addCard(Zone.HAND, playerA, "Angel of Serenity");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
|
||||||
|
//
|
||||||
|
// Create two 2/2 white Knight Ally creature tokens.
|
||||||
|
addCard(Zone.HAND, playerA, "Allied Reinforcements", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 2);
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 2);
|
||||||
|
addCard(Zone.GRAVEYARD, playerB, "Balduvian Bears", 2);
|
||||||
|
|
||||||
|
// create tokens
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Allied Reinforcements");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
|
// angel
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity");
|
||||||
|
setChoice(playerA, "Yes");
|
||||||
|
//addTarget(playerA, "Silvercoat Lion^Balduvian Bears"); // AI must target
|
||||||
|
|
||||||
|
//setStrictChooseMode(true); // AI must target
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Knight Ally", 2);
|
||||||
|
assertPermanentCount(playerA, "Angel of Serenity", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
|
|
||||||
package mage.cards;
|
package mage.cards;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.Abilities;
|
import mage.abilities.Abilities;
|
||||||
|
@ -17,13 +14,16 @@ import mage.game.Game;
|
||||||
import mage.game.GameState;
|
import mage.game.GameState;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface Card extends MageObject {
|
public interface Card extends MageObject {
|
||||||
|
|
||||||
UUID getOwnerId();
|
UUID getOwnerId();
|
||||||
|
|
||||||
String getCardNumber();
|
String getCardNumber();
|
||||||
|
|
||||||
Rarity getRarity();
|
Rarity getRarity(); // null for tokens
|
||||||
|
|
||||||
void setOwnerId(UUID ownerId);
|
void setOwnerId(UUID ownerId);
|
||||||
|
|
||||||
|
@ -77,15 +77,15 @@ public interface Card extends MageObject {
|
||||||
* @param zone
|
* @param zone
|
||||||
* @param sourceId
|
* @param sourceId
|
||||||
* @param game
|
* @param game
|
||||||
* @param flag If zone
|
* @param flag If zone
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
||||||
* bottom</li></ul></li>
|
* bottom</li></ul></li>
|
||||||
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
||||||
* untapped</li></ul></li>
|
* untapped</li></ul></li>
|
||||||
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
||||||
* Battlefield</li></ul></li>
|
* Battlefield</li></ul></li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* @return true if card was moved to zone
|
* @return true if card was moved to zone
|
||||||
*/
|
*/
|
||||||
boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag);
|
boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag);
|
||||||
|
@ -95,8 +95,8 @@ public interface Card extends MageObject {
|
||||||
/**
|
/**
|
||||||
* Moves the card to an exile zone
|
* Moves the card to an exile zone
|
||||||
*
|
*
|
||||||
* @param exileId set to null for generic exile zone
|
* @param exileId set to null for generic exile zone
|
||||||
* @param name used for exile zone with the specified exileId
|
* @param name used for exile zone with the specified exileId
|
||||||
* @param sourceId
|
* @param sourceId
|
||||||
* @param game
|
* @param game
|
||||||
* @return true if card was moved to zone
|
* @return true if card was moved to zone
|
||||||
|
@ -122,7 +122,6 @@ public interface Card extends MageObject {
|
||||||
List<Mana> getMana();
|
List<Mana> getMana();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return true if there exists various art images for this card
|
* @return true if there exists various art images for this card
|
||||||
*/
|
*/
|
||||||
boolean getUsesVariousArt();
|
boolean getUsesVariousArt();
|
||||||
|
@ -149,7 +148,6 @@ public interface Card extends MageObject {
|
||||||
Card copy();
|
Card copy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return The main card of a split half card or adventure spell card, otherwise the card itself is
|
* @return The main card of a split half card or adventure spell card, otherwise the card itself is
|
||||||
* returned
|
* returned
|
||||||
*/
|
*/
|
||||||
|
@ -169,7 +167,7 @@ public interface Card extends MageObject {
|
||||||
|
|
||||||
boolean removeAttachment(UUID permanentId, Game game);
|
boolean removeAttachment(UUID permanentId, Game game);
|
||||||
|
|
||||||
default boolean isOwnedBy(UUID controllerId){
|
default boolean isOwnedBy(UUID controllerId) {
|
||||||
return getOwnerId().equals(controllerId);
|
return getOwnerId().equals(controllerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,22 +186,27 @@ public final class RateCard {
|
||||||
// ratings from card rarity
|
// ratings from card rarity
|
||||||
// some cards can have different rarity -- it's will be used from first set
|
// some cards can have different rarity -- it's will be used from first set
|
||||||
int newRating;
|
int newRating;
|
||||||
switch (card.getRarity()) {
|
if (card.getRarity() != null) {
|
||||||
case COMMON:
|
switch (card.getRarity()) {
|
||||||
newRating = DEFAULT_NOT_RATED_CARD_RATING;
|
case COMMON:
|
||||||
break;
|
newRating = DEFAULT_NOT_RATED_CARD_RATING;
|
||||||
case UNCOMMON:
|
break;
|
||||||
newRating = DEFAULT_NOT_RATED_UNCOMMON_RATING;
|
case UNCOMMON:
|
||||||
break;
|
newRating = DEFAULT_NOT_RATED_UNCOMMON_RATING;
|
||||||
case RARE:
|
break;
|
||||||
newRating = DEFAULT_NOT_RATED_RARE_RATING;
|
case RARE:
|
||||||
break;
|
newRating = DEFAULT_NOT_RATED_RARE_RATING;
|
||||||
case MYTHIC:
|
break;
|
||||||
newRating = DEFAULT_NOT_RATED_MYTHIC_RATING;
|
case MYTHIC:
|
||||||
break;
|
newRating = DEFAULT_NOT_RATED_MYTHIC_RATING;
|
||||||
default:
|
break;
|
||||||
newRating = DEFAULT_NOT_RATED_CARD_RATING;
|
default:
|
||||||
break;
|
newRating = DEFAULT_NOT_RATED_CARD_RATING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// tokens
|
||||||
|
newRating = DEFAULT_NOT_RATED_CARD_RATING;
|
||||||
}
|
}
|
||||||
|
|
||||||
int oldRating = baseRatings.getOrDefault(card.getName(), 0);
|
int oldRating = baseRatings.getOrDefault(card.getName(), 0);
|
||||||
|
|
Loading…
Add table
Reference in a new issue