Ready for Review: Implementing Battles (#10156)

* add types and subtypes

* add startingDefense attribute

* [MOM] Implement Invasion of Ravnica / Guildpact Paragon

* fix two small errors

* refactor various instances of "any target"

* fully implement defense counters

* battles can now be attacked

* [MOM] Implement Invasion of Dominaria / Serra Faithkeeper

* [MOM] Implement Invasion of Innistrad / Deluge of the Dead

* [MOM] Implement Invasion of Kaladesh / Aetherwing, Golden-Scale Flagship

* [MOM] Implement Invasion of Kamigawa / Rooftop Saboteurs

* [MOM] Implement Invasion of Karsus / Refraction Elemental

* [MOM] Implement Invasion of Tolvada / The Broken Sky

* simplify battle info ability

* fix verify failure

* some more fixes for attacking battles

* [MOM] Implement Invasion of Kaldheim / Pyre of the World Tree

* [MOM] Implement Invasion of Lorwyn / Winnowing Forces

* [MOM] Implement Invasion of Moag / Bloomwielder Dryads

* [MOM] Implement Invasion of Shandalar / Leyline Surge

* [MOM] Implement Invasion of Belenon / Belenon War Anthem

* [MOM] Implement Invasion of Pyrulea / Gargantuan Slabhorn

* [MOM] Implement Invasion of Vryn / Overloaded Mage-Ring

* [MOM] Implement Marshal of Zhalfir

* [MOM] Implement Sunfall

* implement protectors for sieges

* partially implement siege defeated trigger

* fix verify failure

* some updates to blocking

* [MOM] Implement Invasion of Mercadia / Kyren Flamewright

* [MOM] Implement Invasion of Theros / Ephara, Ever-Sheltering

* [MOM] Implement Invasion of Ulgrotha / Grandmother Ravi Sengir

* [MOM] Implement Invasion of Xerex / Vertex Paladin

* add initial battle test

* fix verify failure

* [MOM] Implement Invasion of Amonkhet / Lazotep Convert

* [MOM] update spoiler

* update how protectors are chosen

* update text

* battles can't block

* add control change test

* rename battle test for duel

* add multiplayer test

* [MOM] Implement Invasion of Alara / Awaken the Maelstrom

* [MOM] Implement Invasion of Eldraine

* [MOM] Implement Invasion of Ergamon / Truga Cliffhanger

* [MOM] Implement Invasion of Ixalan / Belligerent Regisaur

* battles now cast transformed (this is super hacky but we need to refactor TDFCs anyway)

* add TODO

* add ignore for randomly failing test

* a few small fixes

* add defense to MtgJsonCard (unused like loyalty)

* implement ProtectorIdPredicate

* small fixes
This commit is contained in:
Evan Kranzler 2023-04-13 20:03:16 -04:00 committed by GitHub
parent edf1cff8a8
commit 947351932b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
129 changed files with 4057 additions and 1087 deletions

View file

@ -524,7 +524,13 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
private final CardTypeCounter planeswalkerCounter = new CardTypeCounter() {
@Override
protected boolean is(CardView card) {
return card.isPlanesWalker();
return card.isPlaneswalker();
}
};
private final CardTypeCounter battleCounter = new CardTypeCounter() {
@Override
protected boolean is(CardView card) {
return card.isBattle();
}
};
private final CardTypeCounter tribalCounter = new CardTypeCounter() {
@ -542,6 +548,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
instantCounter,
planeswalkerCounter,
sorceryCounter,
battleCounter,
tribalCounter
};

View file

@ -314,7 +314,7 @@ public class CardViewEDHPowerLevelComparator implements CardViewComparator {
thisMaxPower = Math.max(thisMaxPower, 1);
}
if (card.isPlanesWalker()) {
if (card.isPlaneswalker()) {
thisMaxPower = Math.max(thisMaxPower, 6);
}

View file

@ -313,11 +313,15 @@ public final class GuiDisplayUtil {
}
buffer.append("</td></tr></table>");
String pt = "";
String pt;
if (card.isCreature()) {
pt = card.getPower() + '/' + card.getToughness();
} else if (card.isPlanesWalker()) {
} else if (card.isPlaneswalker()) {
pt = card.getLoyalty();
} else if (card.isBattle()) {
pt = card.getDefense();
} else {
pt = "";
}
buffer.append("<table cellspacing=0 cellpadding=0 border=0 width='100%' valign='bottom'><tr><td><b>");

View file

@ -797,8 +797,10 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
}
if (card.isCreature()) {
sb.append('\n').append(card.getPower()).append('/').append(card.getToughness());
} else if (card.isPlanesWalker()) {
} else if (card.isPlaneswalker()) {
sb.append('\n').append(card.getLoyalty());
} else if (card.isBattle()) {
sb.append('\n').append(card.getDefense());
}
if (card.getRules() == null) {
card.overrideRules(new ArrayList<>());

View file

@ -803,10 +803,14 @@ public class CardPanelRenderModeImage extends CardPanel {
ptText1.setText(getGameCard().getPower());
ptText2.setText("/");
ptText3.setText(CardRendererUtils.getCardLifeWithDamage(getGameCard()));
} else if (card.isPlanesWalker()) {
} else if (card.isPlaneswalker()) {
ptText1.setText("");
ptText2.setText("");
ptText3.setText(getGameCard().getLoyalty());
} else if (card.isBattle()) {
ptText1.setText("");
ptText2.setText("");
ptText3.setText(getGameCard().getDefense());
} else {
ptText1.setText("");
ptText2.setText("");

View file

@ -62,6 +62,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
&& a.getPower().equals(b.getPower())
&& a.getToughness().equals(b.getToughness())
&& a.getLoyalty().equals(b.getLoyalty())
&& a.getDefense().equals(b.getDefense())
&& 0 == a.getColor().compareTo(b.getColor())
&& a.getCardTypes().equals(b.getCardTypes())
&& a.getSubTypes().equals(b.getSubTypes())
@ -128,6 +129,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
sb.append(this.view.getPower());
sb.append(this.view.getToughness());
sb.append(this.view.getLoyalty());
sb.append(this.view.getDefense());
sb.append(this.view.getColor().toString());
sb.append(this.view.getType());
sb.append(this.view.getExpansionSetCode());

View file

@ -530,37 +530,37 @@ public class ModernCardRenderer extends CardRenderer {
g.setPaint(getSpiralLandTextboxColor(twoColors.get(0), twoColors.get(1), true));
// Horizontal bars
g.fillRect(totalContentInset + 1 , typeLineY + boxHeight + 1 , contentWidth - 2 , height_of_spiral);
g.fillRect(totalContentInset + 1 + 2*height_of_spiral, typeLineY + boxHeight + 1 + 2*height_of_spiral , contentWidth - 2 - 4*height_of_spiral , height_of_spiral);
g.fillRect(totalContentInset + 1 + 4*height_of_spiral, typeLineY + boxHeight + 1 + 4*height_of_spiral , contentWidth - 2 - 8*height_of_spiral , height_of_spiral);
g.fillRect(totalContentInset + 1 + 6*height_of_spiral, typeLineY + boxHeight + 1 + 6*height_of_spiral , contentWidth - 2 - 12*height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1, typeLineY + boxHeight + 1, contentWidth - 2, height_of_spiral);
g.fillRect(totalContentInset + 1 + 2 * height_of_spiral, typeLineY + boxHeight + 1 + 2 * height_of_spiral, contentWidth - 2 - 4 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 4 * height_of_spiral, typeLineY + boxHeight + 1 + 4 * height_of_spiral, contentWidth - 2 - 8 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 6 * height_of_spiral, typeLineY + boxHeight + 1 + 6 * height_of_spiral, contentWidth - 2 - 12 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 6*height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 7*height_of_spiral, contentWidth - 2 - 12*height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 4*height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 5*height_of_spiral, contentWidth - 2 - 8*height_of_spiral , height_of_spiral);
g.fillRect(totalContentInset + 1 + 2*height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 3*height_of_spiral, contentWidth - 2 - 4*height_of_spiral , height_of_spiral);
g.fillRect(totalContentInset + 1 , typeLineY + boxHeight + 1 + total_height_of_box - height_of_spiral , contentWidth - 2 , height_of_spiral);
g.fillRect(totalContentInset + 1 + 6 * height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 7 * height_of_spiral, contentWidth - 2 - 12 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 4 * height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 5 * height_of_spiral, contentWidth - 2 - 8 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1 + 2 * height_of_spiral, typeLineY + boxHeight + 1 + total_height_of_box - 3 * height_of_spiral, contentWidth - 2 - 4 * height_of_spiral, height_of_spiral);
g.fillRect(totalContentInset + 1, typeLineY + boxHeight + 1 + total_height_of_box - height_of_spiral, contentWidth - 2, height_of_spiral);
// Vertical bars
g.fillRect(totalContentInset + 1 , typeLineY + boxHeight + 1 , height_of_spiral, total_height_spiral - 1 );
g.fillRect(totalContentInset + 1 + 2*height_of_spiral, typeLineY + boxHeight + 1 + 2*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 4*height_of_spiral );
g.fillRect(totalContentInset + 1 + 4*height_of_spiral, typeLineY + boxHeight + 1 + 4*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 8*height_of_spiral );
g.fillRect(totalContentInset + 1 + 6*height_of_spiral, typeLineY + boxHeight + 1 + 6*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 12*height_of_spiral);
g.fillRect(totalContentInset + 1, typeLineY + boxHeight + 1, height_of_spiral, total_height_spiral - 1);
g.fillRect(totalContentInset + 1 + 2 * height_of_spiral, typeLineY + boxHeight + 1 + 2 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 4 * height_of_spiral);
g.fillRect(totalContentInset + 1 + 4 * height_of_spiral, typeLineY + boxHeight + 1 + 4 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 8 * height_of_spiral);
g.fillRect(totalContentInset + 1 + 6 * height_of_spiral, typeLineY + boxHeight + 1 + 6 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 12 * height_of_spiral);
g.fillRect(totalContentInset + contentWidth - 7*height_of_spiral, typeLineY + boxHeight + 1 + 6*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 12*height_of_spiral);
g.fillRect(totalContentInset + contentWidth - 5*height_of_spiral, typeLineY + boxHeight + 1 + 4*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 8*height_of_spiral );
g.fillRect(totalContentInset + contentWidth - 3*height_of_spiral, typeLineY + boxHeight + 1 + 2*height_of_spiral, height_of_spiral, total_height_spiral - 1 - 4*height_of_spiral );
g.fillRect(totalContentInset + contentWidth - 1*height_of_spiral, typeLineY + boxHeight + 1 + 0*height_of_spiral, height_of_spiral, total_height_spiral - 1 );
g.fillRect(totalContentInset + contentWidth - 7 * height_of_spiral, typeLineY + boxHeight + 1 + 6 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 12 * height_of_spiral);
g.fillRect(totalContentInset + contentWidth - 5 * height_of_spiral, typeLineY + boxHeight + 1 + 4 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 8 * height_of_spiral);
g.fillRect(totalContentInset + contentWidth - 3 * height_of_spiral, typeLineY + boxHeight + 1 + 2 * height_of_spiral, height_of_spiral, total_height_spiral - 1 - 4 * height_of_spiral);
g.fillRect(totalContentInset + contentWidth - 1 * height_of_spiral, typeLineY + boxHeight + 1 + 0 * height_of_spiral, height_of_spiral, total_height_spiral - 1);
}
}
} else {
g.fillRect(
totalContentInset + 1, typeLineY,
contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1);
totalContentInset + 1, typeLineY,
contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1);
}
}
// If it's a planeswalker, extend the textbox left border by some
if (cardView.isPlanesWalker()) {
if (cardView.isPlaneswalker()) {
g.setPaint(borderPaint);
g.fillRect(
totalContentInset, typeLineY + boxHeight,
@ -1116,7 +1116,7 @@ public class ModernCardRenderer extends CardRenderer {
// Is it a walker? (But don't draw the box if it's a non-permanent view
// of a walker without a starting loyalty (EG: Arlin Kord's flipped side).
if (cardView.isPlanesWalker()
if (cardView.isPlaneswalker()
&& (cardView instanceof PermanentView || !cardView.getStartingLoyalty().equals("0"))) {
// Draw the PW loyalty box
int w = partBoxWidth;
@ -1124,26 +1124,15 @@ public class ModernCardRenderer extends CardRenderer {
int x = cardWidth - partBoxWidth - borderWidth;
int y = curY - h;
Polygon symbol = new Polygon(
new int[]{
x + w / 2,
(int) (x + w * 0.9),
x + w,
(int) (x + w * 0.6),
x + w / 2,
(int) (x + w * 0.4),
x,
(int) (x + w * 0.1),},
new int[]{
y + h,
(int) (y + 0.8 * h),
y,
(int) (y - 0.2 * h),
y,
(int) (y - 0.2 * h),
y,
(int) (y + 0.8 * h),},
8);
Polygon symbol = new Polygon();
symbol.addPoint(x + w / 2, y + h);
symbol.addPoint((int) (x + w * 0.9), (int) (y + 0.8 * h));
symbol.addPoint(x + w, y);
symbol.addPoint((int) (x + w * 0.6), (int) (y - 0.2 * h));
symbol.addPoint(x + w / 2, y);
symbol.addPoint((int) (x + w * 0.4), (int) (y - 0.2 * h));
symbol.addPoint(x, y);
symbol.addPoint((int) (x + w * 0.1), (int) (y + 0.8 * h));
// Draw + stroke
g.setColor(Color.black);
@ -1170,6 +1159,59 @@ public class ModernCardRenderer extends CardRenderer {
curY -= (int) (1.2 * y);
}
// Is it a battle?
if (cardView.isBattle()
&& (cardView instanceof PermanentView || !cardView.getStartingDefense().equals("0"))) {
// Draw the PW loyalty box
int w = 3 * partBoxWidth / 4;
int h = 3 * partBoxWidth / 4;
int x = cardWidth - w - borderWidth;
int y = curY - h;
Polygon symbol = new Polygon();
symbol.addPoint(x + (0 * w) / 80, y + (2 * h) / 80);
symbol.addPoint(x + (12 * w) / 80, y + (30 * h) / 80);
symbol.addPoint(x + (3 * w) / 80, y + (40 * h) / 80);
symbol.addPoint(x + (12 * w) / 80, y + (50 * h) / 80);
symbol.addPoint(x + (0 * w) / 80, y + (78 * h) / 80);
symbol.addPoint(x + (30 * w) / 80, y + (71 * h) / 80);
symbol.addPoint(x + (40 * w) / 80, y + (80 * h) / 80);
symbol.addPoint(x + (50 * w) / 80, y + (71 * h) / 80);
symbol.addPoint(x + (80 * w) / 80, y + (78 * h) / 80);
symbol.addPoint(x + (68 * w) / 80, y + (50 * h) / 80);
symbol.addPoint(x + (77 * w) / 80, y + (40 * h) / 80);
symbol.addPoint(x + (68 * w) / 80, y + (30 * h) / 80);
symbol.addPoint(x + (80 * w) / 80, y + (2 * h) / 80);
symbol.addPoint(x + (48 * w) / 80, y + (9 * h) / 80);
symbol.addPoint(x + (40 * w) / 80, y + (0 * h) / 80);
symbol.addPoint(x + (32 * w) / 80, y + (9 * h) / 80);
// Draw + stroke
g.setColor(Color.black);
g.fillPolygon(symbol);
g.setColor(new Color(200, 200, 200));
g.setStroke(new BasicStroke(2));
g.drawPolygon(symbol);
g.setStroke(new BasicStroke(1));
// Loyalty number
String defense;
if (cardView instanceof PermanentView) {
defense = cardView.getDefense();
} else {
defense = cardView.getStartingDefense();
}
g.setFont(ptTextFont);
g.setColor(Color.white);
int defenseWidth = g.getFontMetrics().stringWidth(defense);
g.drawString(defense, x + 1 + (w - defenseWidth) / 2, y - 1 + ptTextHeight + (h - ptTextHeight) / 2);
// Advance
curY -= (int) (1.2 * y);
}
// does it have damage on it?
if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) {
int x = cardWidth - partBoxWidth - borderWidth;
@ -1769,7 +1811,7 @@ public class ModernCardRenderer extends CardRenderer {
private static Color getLessOpaqueColor(Color color, boolean lessOpaqueRulesTextBox) {
if (lessOpaqueRulesTextBox) {
Color lessOpaque = new Color (color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() - 50);
Color lessOpaque = new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() - 50);
return lessOpaque;
}
return color;