mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
[PIP] Implement Rad Counters mechanic (#12087)
This commit is contained in:
parent
11373fd75d
commit
9d7bf27d38
14 changed files with 420 additions and 176 deletions
|
|
@ -1,6 +1,5 @@
|
|||
package mage.client.game;
|
||||
|
||||
import mage.cards.decks.importer.DckDeckImporter;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.SessionHandler;
|
||||
import mage.client.cards.BigCard;
|
||||
|
|
@ -8,7 +7,6 @@ import mage.client.components.HoverButton;
|
|||
import mage.client.components.MageRoundPane;
|
||||
import mage.client.components.ext.dlg.DialogManager;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.themes.ThemeType;
|
||||
import mage.client.util.CardsViewUtil;
|
||||
import mage.client.util.ImageHelper;
|
||||
import mage.client.util.gui.BufferedImageBuilder;
|
||||
|
|
@ -16,6 +14,7 @@ import mage.client.util.gui.countryBox.CountryUtil;
|
|||
import mage.components.ImagePanel;
|
||||
import mage.components.ImagePanelStyle;
|
||||
import mage.constants.CardType;
|
||||
import static mage.constants.Constants.*;
|
||||
import mage.constants.ManaType;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
|
|
@ -33,12 +32,10 @@ import java.awt.*;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.util.*;
|
||||
|
||||
import static mage.constants.Constants.*;
|
||||
|
||||
/**
|
||||
* Game GUI: player panel with avatar and icons
|
||||
*
|
||||
* @author nantuko, JayDi85
|
||||
* @author nantuko, JayDi85, Susucr
|
||||
*/
|
||||
public class PlayerPanelExt extends javax.swing.JPanel {
|
||||
|
||||
|
|
@ -53,8 +50,8 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
private static final String DEFAULT_AVATAR_PATH = "/avatars/" + DEFAULT_AVATAR_ID + ".jpg";
|
||||
|
||||
private static final int PANEL_WIDTH = 94;
|
||||
private static final int PANEL_HEIGHT = 262;
|
||||
private static final int PANEL_HEIGHT_SMALL = 210;
|
||||
private static final int PANEL_HEIGHT = 290;
|
||||
private static final int PANEL_HEIGHT_SMALL = 238;
|
||||
private static final int PANEL_HEIGHT_EXTRA_FOR_ME = 25;
|
||||
private static final int MANA_LABEL_SIZE_HORIZONTAL = 20;
|
||||
|
||||
|
|
@ -232,6 +229,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
setTextForLabel("poison", poisonLabel, poison, player.getCounters().getCount(CounterType.POISON), false);
|
||||
setTextForLabel("energy", energyLabel, energy, player.getCounters().getCount(CounterType.ENERGY), false);
|
||||
setTextForLabel("experience", experienceLabel, experience, player.getCounters().getCount(CounterType.EXPERIENCE), false);
|
||||
setTextForLabel("rad", radLabel, rad, player.getCounters().getCount(CounterType.RAD), false);
|
||||
setTextForLabel("hand zone", handLabel, hand, player.getHandCount(), true);
|
||||
int libraryCards = player.getLibraryCount();
|
||||
if (libraryCards > 99) {
|
||||
|
|
@ -493,6 +491,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
poisonLabel = new JLabel();
|
||||
energyLabel = new JLabel();
|
||||
experienceLabel = new JLabel();
|
||||
radLabel = new JLabel();
|
||||
graveLabel = new JLabel();
|
||||
commandLabel = new JLabel();
|
||||
libraryLabel = new JLabel();
|
||||
|
|
@ -523,14 +522,17 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
// life area
|
||||
r = new Rectangle(18, 18);
|
||||
lifeLabel.setToolTipText("Life");
|
||||
lifeLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
Image imageLife = ImageHelper.getImageFromResources("/info/life.png");
|
||||
BufferedImage resizedLife = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imageLife, BufferedImage.TYPE_INT_ARGB), r);
|
||||
life = new ImagePanel(resizedLife, ImagePanelStyle.ACTUAL);
|
||||
life.setToolTipText("Life");
|
||||
life.setOpaque(false);
|
||||
|
||||
// hand area
|
||||
r = new Rectangle(18, 18);
|
||||
handLabel.setToolTipText("Hand");
|
||||
handLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
Image imageHand = ImageHelper.getImageFromResources("/info/hand.png");
|
||||
BufferedImage resizedHand = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imageHand, BufferedImage.TYPE_INT_ARGB), r);
|
||||
hand = new ImagePanel(resizedHand, ImagePanelStyle.ACTUAL);
|
||||
|
|
@ -544,10 +546,12 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
poison = new ImagePanel(resizedPoison, ImagePanelStyle.ACTUAL);
|
||||
poison.setOpaque(false);
|
||||
setTextForLabel("poison", poisonLabel, poison, 0, false);
|
||||
poisonLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
// Library
|
||||
r = new Rectangle(19, 19);
|
||||
libraryLabel.setToolTipText("Library");
|
||||
libraryLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
Image imageLibrary = ImageHelper.getImageFromResources("/info/library.png");
|
||||
BufferedImage resizedLibrary = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imageLibrary, BufferedImage.TYPE_INT_ARGB), r);
|
||||
|
||||
|
|
@ -559,6 +563,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
// Grave count and open graveyard button
|
||||
r = new Rectangle(21, 21);
|
||||
graveLabel.setToolTipText("Card Types: 0");
|
||||
graveLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
Image imageGrave = ImageHelper.getImageFromResources("/info/grave.png");
|
||||
BufferedImage resizedGrave = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imageGrave, BufferedImage.TYPE_INT_ARGB), r);
|
||||
|
||||
|
|
@ -569,6 +574,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
exileLabel = new JLabel();
|
||||
exileLabel.setToolTipText("Exile");
|
||||
exileLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
image = ImageHelper.getImageFromResources("/info/exile.png");
|
||||
r = new Rectangle(21, 21);
|
||||
resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||
|
|
@ -619,12 +625,6 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
toolHintsHelper.setBounds(3, 2 + 21 + 2, 73, 21);
|
||||
zonesPanel.add(toolHintsHelper);
|
||||
|
||||
energyExperiencePanel = new JPanel();
|
||||
energyExperiencePanel.setPreferredSize(new Dimension(100, 20));
|
||||
energyExperiencePanel.setSize(100, 20);
|
||||
energyExperiencePanel.setLayout(null);
|
||||
energyExperiencePanel.setOpaque(false);
|
||||
|
||||
// Energy count
|
||||
r = new Rectangle(18, 18);
|
||||
Image imageEnergy = ImageHelper.getImageFromResources("/info/energy.png");
|
||||
|
|
@ -633,6 +633,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
energy.setToolTipText("Energy");
|
||||
energy.setOpaque(false);
|
||||
setTextForLabel("energy", energyLabel, energy, 0, false);
|
||||
energyLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
// Experience count
|
||||
r = new Rectangle(18, 18);
|
||||
|
|
@ -642,6 +643,17 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
experience.setToolTipText("Experience");
|
||||
experience.setOpaque(false);
|
||||
setTextForLabel("experience", experienceLabel, experience, 0, false);
|
||||
experienceLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
// Rad count
|
||||
r = new Rectangle(16, 16);
|
||||
Image imageRad = ImageHelper.getImageFromResources("/info/rad.png");
|
||||
BufferedImage resizedRad = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imageRad, BufferedImage.TYPE_INT_ARGB), r);
|
||||
rad = new ImagePanel(resizedRad, ImagePanelStyle.ACTUAL);
|
||||
rad.setToolTipText("Rad");
|
||||
rad.setOpaque(false);
|
||||
setTextForLabel("rad", radLabel, rad, 0, false);
|
||||
radLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
btnPlayer = new JButton();
|
||||
btnPlayer.setText("Player");
|
||||
|
|
@ -672,6 +684,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
//*/
|
||||
///*
|
||||
JLabel manaCountLabelW = new JLabel();
|
||||
manaCountLabelW.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
manaLabels.put(manaCountLabelW, ManaType.WHITE);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaW = ManaSymbols.getSizedManaSymbol("W", 15);
|
||||
|
|
@ -684,6 +697,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
JLabel manaCountLabelU = new JLabel();
|
||||
manaLabels.put(manaCountLabelU, ManaType.BLUE);
|
||||
manaCountLabelU.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaU = ManaSymbols.getSizedManaSymbol("U", 15);
|
||||
HoverButton btnBlueMana = new HoverButton(null, imageManaU, imageManaU, imageManaU, r);
|
||||
|
|
@ -694,6 +708,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
JLabel manaCountLabelB = new JLabel();
|
||||
manaLabels.put(manaCountLabelB, ManaType.BLACK);
|
||||
manaCountLabelB.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaB = ManaSymbols.getSizedManaSymbol("B", 15);
|
||||
HoverButton btnBlackMana = new HoverButton(null, imageManaB, imageManaB, imageManaB, r);
|
||||
|
|
@ -704,6 +719,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
JLabel manaCountLabelR = new JLabel();
|
||||
manaLabels.put(manaCountLabelR, ManaType.RED);
|
||||
manaCountLabelR.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaR = ManaSymbols.getSizedManaSymbol("R", 15);
|
||||
HoverButton btnRedMana = new HoverButton(null, imageManaR, imageManaR, imageManaR, r);
|
||||
|
|
@ -714,6 +730,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
JLabel manaCountLabelG = new JLabel();
|
||||
manaLabels.put(manaCountLabelG, ManaType.GREEN);
|
||||
manaCountLabelG.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaG = ManaSymbols.getSizedManaSymbol("G", 15);
|
||||
HoverButton btnGreenMana = new HoverButton(null, imageManaG, imageManaG, imageManaG, r);
|
||||
|
|
@ -724,6 +741,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
JLabel manaCountLabelX = new JLabel();
|
||||
manaLabels.put(manaCountLabelX, ManaType.COLORLESS);
|
||||
manaCountLabelX.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
r = new Rectangle(15, 15);
|
||||
BufferedImage imageManaX = ManaSymbols.getSizedManaSymbol("C", 15);
|
||||
HoverButton btnColorlessMana = new HoverButton(null, imageManaX, imageManaX, imageManaX, r);
|
||||
|
|
@ -735,90 +753,6 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
GroupLayout gl_panelBackground = new GroupLayout(panelBackground);
|
||||
gl_panelBackground.setHorizontalGroup(
|
||||
gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(9)
|
||||
.addComponent(life, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(3)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(18)
|
||||
.addComponent(hand, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lifeLabel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(4)
|
||||
.addComponent(handLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(9)
|
||||
.addComponent(poison, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(3)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(18)
|
||||
.addComponent(library, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(poisonLabel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(4)
|
||||
.addComponent(libraryLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(9)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(energy, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnBlueMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnBlackMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(grave, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(18)
|
||||
.addComponent(experience, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(energyLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(20)
|
||||
.addComponent(btnRedMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(manaCountLabelW, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(3)
|
||||
.addComponent(manaCountLabelR, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addComponent(manaCountLabelB, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(19)
|
||||
.addComponent(btnColorlessMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(5)
|
||||
.addComponent(manaCountLabelX, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(20)
|
||||
.addComponent(btnGreenMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(40)
|
||||
.addComponent(manaCountLabelG, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(40)
|
||||
.addComponent(experienceLabel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(18)
|
||||
.addComponent(exileZone, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(5)
|
||||
.addComponent(graveLabel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(40)
|
||||
.addComponent(exileLabel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(manaCountLabelU, GroupLayout.PREFERRED_SIZE, MANA_LABEL_SIZE_HORIZONTAL, GroupLayout.PREFERRED_SIZE))))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(6)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
|
|
@ -826,11 +760,68 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
.addComponent(timerLabel, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(avatar, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE))
|
||||
.addGap(8))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(9)
|
||||
// The left column of icon+label
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(life, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(lifeLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(poison, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(poisonLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(energy, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(energyLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(rad, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(radLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelW, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnBlueMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelU, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnBlackMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelB, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(grave, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(graveLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)))
|
||||
// The right column of icon+label
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(hand, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(handLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(library, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(libraryLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(experience, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(experienceLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnRedMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelR, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnGreenMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelG, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(btnColorlessMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(manaCountLabelX, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addComponent(exileZone, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(exileLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(4))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(6)
|
||||
.addComponent(zonesPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(14))
|
||||
);
|
||||
.addGap(6)));
|
||||
gl_panelBackground.setVerticalGroup(
|
||||
gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
|
|
@ -845,85 +836,77 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(life, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lifeLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(hand, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(lifeLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(handLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
// Poison & Library
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(poison, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(poisonLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(library, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(poisonLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(libraryLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(1)
|
||||
// Poison
|
||||
// Energy & Experience
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(2)
|
||||
.addComponent(energy, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(2)
|
||||
.addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(2)
|
||||
.addComponent(btnBlueMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(2)
|
||||
.addComponent(btnBlackMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(3)
|
||||
.addComponent(grave, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
.addGap(1)
|
||||
.addComponent(energy, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(energyLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(experience, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(energyLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(2)
|
||||
.addComponent(btnRedMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(14)
|
||||
.addComponent(manaCountLabelW, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(14)
|
||||
.addComponent(manaCountLabelR, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(4)
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addComponent(manaCountLabelB, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(8)
|
||||
.addComponent(btnColorlessMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelX, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(1)
|
||||
.addComponent(experience, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(experienceLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
// Rad & <empty>
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(39)
|
||||
.addGap(1)
|
||||
.addComponent(rad, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(radLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
// W & R
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelW, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(btnRedMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelR, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE))
|
||||
// U & G
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(btnBlueMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelU, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(1)
|
||||
.addComponent(btnGreenMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelG, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE))
|
||||
// B & X
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(31)
|
||||
.addComponent(manaCountLabelG, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(experienceLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(1)
|
||||
.addComponent(btnBlackMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelB, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(76)
|
||||
.addComponent(exileZone, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
.addGap(1)
|
||||
.addComponent(btnColorlessMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(manaCountLabelX, GroupLayout.PREFERRED_SIZE, 17, GroupLayout.PREFERRED_SIZE))
|
||||
// grave & exile
|
||||
.addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(76)
|
||||
.addComponent(graveLabel, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(grave, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(graveLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(76)
|
||||
.addComponent(exileLabel, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(gl_panelBackground.createSequentialGroup()
|
||||
.addGap(31)
|
||||
.addComponent(manaCountLabelU, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
)
|
||||
.addComponent(exileZone, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(exileLabel, GroupLayout.PREFERRED_SIZE, 20, GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(2)
|
||||
.addComponent(zonesPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
)
|
||||
);
|
||||
.addComponent(zonesPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)));
|
||||
panelBackground.setLayout(gl_panelBackground);
|
||||
GroupLayout groupLayout = new GroupLayout(this);
|
||||
groupLayout.setHorizontalGroup(
|
||||
|
|
@ -1013,6 +996,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
private ImagePanel poison;
|
||||
private ImagePanel energy;
|
||||
private ImagePanel experience;
|
||||
private ImagePanel rad;
|
||||
private ImagePanel hand;
|
||||
private HoverButton grave;
|
||||
private HoverButton library;
|
||||
|
|
@ -1027,12 +1011,12 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
private JLabel poisonLabel;
|
||||
private JLabel energyLabel;
|
||||
private JLabel experienceLabel;
|
||||
private JLabel radLabel;
|
||||
private JLabel graveLabel;
|
||||
private JLabel commandLabel;
|
||||
private JLabel exileLabel;
|
||||
|
||||
private JPanel zonesPanel;
|
||||
private JPanel energyExperiencePanel;
|
||||
private HoverButton exileZone;
|
||||
private HoverButton commandZone;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
Mage.Client/src/main/resources/info/rad.png
Normal file
BIN
Mage.Client/src/main/resources/info/rad.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -34,7 +34,7 @@ public final class SurvivorsMedKit extends CardImpl {
|
|||
Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(1));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.withFirstModeFlavorWord("Stimpak");
|
||||
ability.getModes().setLimitUsageByOnce(true);
|
||||
ability.getModes().setLimitUsageByOnce(false);
|
||||
|
||||
// * Fancy Lads Snack Cakes -- Create a Food token.
|
||||
ability.addMode(new Mode(new CreateTokenEffect(new FoodToken())).withFlavorWord("Fancy Lads Snack Cakes"));
|
||||
|
|
@ -44,6 +44,7 @@ public final class SurvivorsMedKit extends CardImpl {
|
|||
.addEffect(new SacrificeSourceEffect())
|
||||
.addTarget(new TargetPlayer())
|
||||
.withFlavorWord("RadAway"));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private SurvivorsMedKit(final SurvivorsMedKit card) {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,11 @@ import mage.cards.ExpansionSet;
|
|||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class Fallout extends ExpansionSet {
|
||||
|
||||
// skip list for rad counter mechanic
|
||||
private static final List<String> unfinished = Arrays.asList("Acquired Mutation", "Bloatfly Swarm", "Contaminated Drink", "Feral Ghoul", "Glowing One", "Harold and Bob, First Numens", "Infesting Radroach", "Mariposa Military Base", "Megaton's Fate", "Mirelurk Queen", "Nightkin Ambusher", "Nuclear Fallout", "Nuka-Nuke Launcher", "Screeching Scorchbeast", "Strong, the Brutish Thespian", "Struggle for Project Purity", "Survivor's Med Kit", "Tato Farmer", "The Master, Transcendent", "The Wise Mothman", "Vault 12: The Necropolis", "Vexing Radgull");
|
||||
private static final Fallout instance = new Fallout();
|
||||
|
||||
public static Fallout getInstance() {
|
||||
|
|
@ -365,7 +360,5 @@ public final class Fallout extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Windbrisk Heights", 315, Rarity.RARE, mage.cards.w.WindbriskHeights.class));
|
||||
cards.add(new SetCardInfo("Winding Constrictor", 223, Rarity.UNCOMMON, mage.cards.w.WindingConstrictor.class));
|
||||
cards.add(new SetCardInfo("Woodland Cemetery", 316, Rarity.RARE, mage.cards.w.WoodlandCemetery.class));
|
||||
|
||||
cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); // remove when mechanic is implemented
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase {
|
|||
assertLife(playerB, 18); // -2 from Robber
|
||||
|
||||
// possible bug: triggers from destroyed permanents keeps in game state (e.g. 2 triggers in game state)
|
||||
Assert.assertEquals("game state must have only 1 trigger from original card", 1, currentGame.getState().getTriggers().size());
|
||||
// (Since the introduction of Rad Counters, there is one inherent trigger per player in the state, hence the "+ 2")
|
||||
Assert.assertEquals("game state must have only 1 trigger from original card", 1 + 2, currentGame.getState().getTriggers().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
|
@ -16,7 +15,6 @@ public class MephidrossVampireTest extends CardTestPlayerBase {
|
|||
|
||||
/**
|
||||
* Checks if only one triggered ability is handleded in the pool
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testMultiTriggers() {
|
||||
|
|
@ -35,9 +33,10 @@ public class MephidrossVampireTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertPermanentCount(playerB, "Mephidross Vampire", 1);
|
||||
assertPowerToughness(playerB, "Mephidross Vampire", 4, 5);
|
||||
assertPowerToughness(playerB, "Mephidross Vampire", 4, 5);
|
||||
|
||||
// (Since the introduction of Rad Counters, there is one inherent trigger per player in the state, hence the "+ 2")
|
||||
Assert.assertEquals("There should only be one triggered ability in the list of triggers of the State", 1 + 2, currentGame.getState().getTriggers().size());
|
||||
|
||||
Assert.assertEquals("There should only be one triggered ability in the list of triggers of the State", 1, currentGame.getState().getTriggers().size());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
package org.mage.test.cards.single.pip;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.players.Player;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* > 725.1. Rad counters are a kind of counter a player can have (see rule 122, "Counters").
|
||||
* There is an inherent triggered ability associated with rad counters. This ability has no
|
||||
* source and is controlled by the active player. This is an exception to rule 113.8. The full
|
||||
* text of this ability is "At the beginning of each player's precombat main phase, if that
|
||||
* player has one or more rad counters, that player mills a number of cards equal to the number
|
||||
* of rad counters they have. For each nonland card milled this way, that player loses 1 life
|
||||
* and removes one rad counter from themselves."
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class RadCounterTriggerTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.n.NuclearFallout} {X}{B}{B}
|
||||
* Sorcery
|
||||
* Each creature gets twice -X/-X until end of turn. Each player gets X rad counters.
|
||||
*/
|
||||
private static final String fallout = "Nuclear Fallout";
|
||||
|
||||
private static void checkRadCounterCount(String message, Player player, int expected) {
|
||||
Assert.assertEquals(message, expected, player.getCounters().getCount(CounterType.RAD));
|
||||
}
|
||||
|
||||
private static void checkGraveyardSize(String message, Player player, int expected) {
|
||||
Assert.assertEquals(message, expected, player.getGraveyard().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Fallout_3_Multiple_Turns() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.HAND, playerA, fallout);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
//addCard(Zone.BATTLEFIELD, playerB, "Blinkmoth Urn");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Yawgmoth's Bargain"); // Skip draw step, just to simplify library setup.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Yawgmoth's Bargain"); // Skip draw step, just to simplify library setup.
|
||||
|
||||
// Library setup for player A:
|
||||
// Turn 7, 1 rad counter, mill 1 land, final check: 1 rad counter
|
||||
addCard(Zone.LIBRARY, playerA, "Tundra");
|
||||
// Turn 5, 3 rad counters, mill 1 land 2 nonland
|
||||
addCard(Zone.LIBRARY, playerA, "Akoum Warrior");
|
||||
addCard(Zone.LIBRARY, playerA, "Tropical Island");
|
||||
addCard(Zone.LIBRARY, playerA, "Lightning Bolt");
|
||||
// Turn 3, 3 rad counters, mill only lands
|
||||
addCard(Zone.LIBRARY, playerA, "Bayou", 3);
|
||||
|
||||
// Library setup for player B:
|
||||
// Turn 6, 0 rad counter, no trigger.
|
||||
// Turn 4, 2 rad counters, mill 2 nonlands
|
||||
addCard(Zone.LIBRARY, playerB, "Elite Vanguard");
|
||||
addCard(Zone.LIBRARY, playerB, "Fire // Ice");
|
||||
// Turn 2, 3 rad counters, mill 2 lands, 1 nonland
|
||||
addCard(Zone.LIBRARY, playerB, "Brainstorm");
|
||||
addCard(Zone.LIBRARY, playerB, "Plains", 2);
|
||||
|
||||
setChoice(playerA, "X=3");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fallout);
|
||||
|
||||
runCode("rad count playerA turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkRadCounterCount(info, player, 3));
|
||||
runCode("rad count playerB turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkRadCounterCount(info, player, 3));
|
||||
|
||||
// Turn 2 -- start 0 gy, 3 rad, mill 1 nonland 2 land
|
||||
runCode("rad count playerB turn 2", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkRadCounterCount(info, player, 2));
|
||||
checkLife("life playerB turn 2", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, 19);
|
||||
runCode("graveyard count playerB turn 2", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkGraveyardSize(info, player, 3));
|
||||
|
||||
// Turn 3 -- start 1 gy, 3 rad, mill 3 nonland
|
||||
runCode("rad count playerA turn 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkRadCounterCount(info, player, 3));
|
||||
checkLife("life playerA turn 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, 20);
|
||||
runCode("graveyard count playerA turn 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkGraveyardSize(info, player, 4));
|
||||
|
||||
// Turn 4 -- start 3gy, 2 rad, mill 2 nonland
|
||||
runCode("rad count playerB turn 4", 4, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkRadCounterCount(info, player, 0));
|
||||
checkLife("life playerB turn 4", 4, PhaseStep.POSTCOMBAT_MAIN, playerB, 17);
|
||||
runCode("graveyard count playerB turn 4", 4, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkGraveyardSize(info, player, 5));
|
||||
|
||||
// Turn 5 -- start 4 gy, 3 rad, mill 2 nonland 1 land
|
||||
runCode("rad count playerA turn 5", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkRadCounterCount(info, player, 1));
|
||||
checkLife("life playerA turn 5", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, 18);
|
||||
runCode("graveyard count playerA turn 5", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkGraveyardSize(info, player, 7));
|
||||
|
||||
// Turn 6 -- start 5gy, no rad
|
||||
runCode("rad count playerB turn 6", 6, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkRadCounterCount(info, player, 0));
|
||||
checkLife("life playerB turn 6", 6, PhaseStep.POSTCOMBAT_MAIN, playerB, 17);
|
||||
runCode("graveyard count playerB turn 6", 6, PhaseStep.POSTCOMBAT_MAIN, playerB, (info, player, game) -> checkGraveyardSize(info, player, 5));
|
||||
|
||||
// Turn 7 -- start 7 gy, 1 rad, mill 1 land
|
||||
runCode("rad count playerA turn 7", 7, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkRadCounterCount(info, player, 1));
|
||||
checkLife("life playerA turn 7", 7, PhaseStep.POSTCOMBAT_MAIN, playerA, 18);
|
||||
runCode("graveyard count playerA turn 7", 7, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> checkGraveyardSize(info, player, 8));
|
||||
|
||||
setStopAt(7, PhaseStep.END_TURN);
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -308,10 +308,9 @@ public class TriggeredAbilities extends LinkedHashMap<String, TriggeredAbility>
|
|||
|
||||
public void removeAbilitiesOfNonExistingSources(Game game) {
|
||||
// e.g. Token that had triggered abilities
|
||||
|
||||
entrySet().removeIf(entry -> game.getObject(entry.getValue().getSourceId()) == null
|
||||
&& game.getState().getInherentEmblems().stream().noneMatch(emblem -> emblem.getId().equals(entry.getValue().getSourceId()))
|
||||
&& game.getState().getDesignations().stream().noneMatch(designation -> designation.getId().equals(entry.getValue().getSourceId())));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public enum TokenRepository {
|
|||
public static final String XMAGE_IMAGE_NAME_DAY = "Day";
|
||||
public static final String XMAGE_IMAGE_NAME_NIGHT = "Night";
|
||||
public static final String XMAGE_IMAGE_NAME_THE_MONARCH = "The Monarch";
|
||||
public static final String XMAGE_IMAGE_NAME_RADIATION = "Radiation";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TokenRepository.class);
|
||||
|
||||
|
|
@ -296,6 +297,9 @@ public enum TokenRepository {
|
|||
res.add(createXmageToken(XMAGE_IMAGE_NAME_THE_MONARCH, 2, "https://api.scryfall.com/cards/tcn2/1/en?format=image"));
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_THE_MONARCH, 3, "https://api.scryfall.com/cards/tltc/15/en?format=image"));
|
||||
|
||||
// Radiation (for trigger)
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_RADIATION, 1, "https://api.scryfall.com/cards/tpip/22/en?format=image"));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import mage.game.combat.CombatGroup;
|
|||
import mage.game.command.*;
|
||||
import mage.game.command.dungeons.UndercityDungeon;
|
||||
import mage.game.command.emblems.EmblemOfCard;
|
||||
import mage.game.command.emblems.RadiationEmblem;
|
||||
import mage.game.command.emblems.TheRingEmblem;
|
||||
import mage.game.events.*;
|
||||
import mage.game.events.TableEvent.EventType;
|
||||
|
|
@ -443,6 +444,11 @@ public abstract class GameImpl implements Game {
|
|||
return designation;
|
||||
}
|
||||
}
|
||||
for (Emblem emblem : state.getInherentEmblems()) {
|
||||
if (emblem.getId().equals(objectId)) {
|
||||
return emblem;
|
||||
}
|
||||
}
|
||||
// can be an ability of a sacrificed Token trying to get it's source object
|
||||
object = getLastKnownInformation(objectId, Zone.BATTLEFIELD);
|
||||
}
|
||||
|
|
@ -1366,6 +1372,14 @@ public abstract class GameImpl implements Game {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rad counter mechanic for every player
|
||||
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
|
||||
// This is not a real emblem. Just a fake source for the
|
||||
// inherent trigger ability related to Rad counters
|
||||
// Faking a source just to display something on the stack ability.
|
||||
state.addInherentEmblem(new RadiationEmblem(), playerId);
|
||||
}
|
||||
}
|
||||
|
||||
public void initGameDefaultWatchers() {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import mage.game.combat.Combat;
|
|||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.command.Command;
|
||||
import mage.game.command.CommandObject;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.command.Plane;
|
||||
import mage.game.events.*;
|
||||
import mage.game.permanent.Battlefield;
|
||||
|
|
@ -85,6 +86,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
private boolean isPlaneChase;
|
||||
private List<String> seenPlanes = new ArrayList<>();
|
||||
private List<Designation> designations = new ArrayList<>();
|
||||
private List<Emblem> inherentEmblems = new ArrayList<>();
|
||||
private Exile exile;
|
||||
private Battlefield battlefield;
|
||||
private int turnNum = 1;
|
||||
|
|
@ -157,6 +159,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.isPlaneChase = state.isPlaneChase;
|
||||
this.seenPlanes.addAll(state.seenPlanes);
|
||||
this.designations.addAll(state.designations);
|
||||
this.inherentEmblems = CardUtil.deepCopyObject(state.inherentEmblems);
|
||||
this.exile = state.exile.copy();
|
||||
this.battlefield = state.battlefield.copy();
|
||||
this.turnNum = state.turnNum;
|
||||
|
|
@ -204,6 +207,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
exile.clear();
|
||||
command.clear();
|
||||
designations.clear();
|
||||
inherentEmblems.clear();
|
||||
seenPlanes.clear();
|
||||
isPlaneChase = false;
|
||||
revealed.clear();
|
||||
|
|
@ -245,6 +249,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.isPlaneChase = state.isPlaneChase;
|
||||
this.seenPlanes = state.seenPlanes;
|
||||
this.designations = state.designations;
|
||||
this.inherentEmblems = state.inherentEmblems;
|
||||
this.exile = state.exile;
|
||||
this.battlefield = state.battlefield;
|
||||
this.turnNum = state.turnNum;
|
||||
|
|
@ -506,6 +511,10 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
return designations;
|
||||
}
|
||||
|
||||
public List<Emblem> getInherentEmblems() {
|
||||
return inherentEmblems;
|
||||
}
|
||||
|
||||
public Plane getCurrentPlane() {
|
||||
if (command != null && command.size() > 0) {
|
||||
for (CommandObject cobject : command) {
|
||||
|
|
@ -1135,6 +1144,25 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherent triggers (Rad counters) in the rules have no source.
|
||||
* However to fit better with the engine, we make a fake emblem source,
|
||||
* which is not displayed in any game zone. That allows the trigger to
|
||||
* have a source, which helps with a bunch of situation like hosting,
|
||||
* rather than having a trigger.
|
||||
* <p>
|
||||
* Should not be used except in very specific situations
|
||||
*/
|
||||
public void addInherentEmblem(Emblem emblem, UUID controllerId) {
|
||||
getInherentEmblems().add(emblem);
|
||||
emblem.setControllerId(controllerId);
|
||||
for (Ability ability : emblem.getInitAbilities()) {
|
||||
ability.setControllerId(controllerId);
|
||||
ability.setSourceId(emblem.getId());
|
||||
addAbility(ability, null, emblem);
|
||||
}
|
||||
}
|
||||
|
||||
public void addDesignation(Designation designation, Game game, UUID controllerId) {
|
||||
getDesignations().add(designation);
|
||||
for (Ability ability : designation.getInitAbilities()) {
|
||||
|
|
@ -1414,7 +1442,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
/**
|
||||
* Store the tags of source ability using the MOR as a reference
|
||||
*/
|
||||
void storePermanentCostsTags(MageObjectReference permanentMOR, Ability source){
|
||||
void storePermanentCostsTags(MageObjectReference permanentMOR, Ability source) {
|
||||
if (source.getCostsTagMap() != null) {
|
||||
permanentCostsTags.put(permanentMOR, CardUtil.deepCopyObject(source.getCostsTagMap()));
|
||||
}
|
||||
|
|
@ -1424,9 +1452,9 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
* Removes the cost tags if the corresponding permanent is no longer on the battlefield.
|
||||
* Only use if the stack is empty and nothing can refer to them anymore (such as at EOT, the current behavior)
|
||||
*/
|
||||
public void cleanupPermanentCostsTags(Game game){
|
||||
public void cleanupPermanentCostsTags(Game game) {
|
||||
getPermanentCostsTags().entrySet().removeIf(entry ->
|
||||
!(entry.getKey().getZoneChangeCounter() == game.getState().getZoneChangeCounter(entry.getKey().getSourceId())-1)
|
||||
!(entry.getKey().getZoneChangeCounter() == game.getState().getZoneChangeCounter(entry.getKey().getSourceId()) - 1)
|
||||
); // The stored MOR is the stack-moment MOR so need to subtract one from the permanent's ZCC for the check
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
this.controllerId = emblem.controllerId;
|
||||
this.sourceObject = emblem.sourceObject;
|
||||
this.copy = emblem.copy;
|
||||
this.copyFrom = (emblem.copyFrom != null ? emblem.copyFrom : null);
|
||||
this.copyFrom = emblem.copyFrom;
|
||||
this.abilites = emblem.abilites.copy();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
package mage.game.command.emblems;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.repository.TokenInfo;
|
||||
import mage.cards.repository.TokenRepository;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* Special emblem to enable the Rad Counter inherent trigger
|
||||
* with an actual source, to display image on the stack.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class RadiationEmblem extends Emblem {
|
||||
|
||||
public RadiationEmblem() {
|
||||
super("Radiation");
|
||||
|
||||
this.getAbilities().add(new ConditionalInterveningIfTriggeredAbility(
|
||||
new BeginningOfPreCombatMainTriggeredAbility(Zone.ALL, new RadiationEffect(), TargetController.YOU, false, false),
|
||||
RadiationCondition.instance,
|
||||
"At the beginning of your precombat main phase, if you have any rad counters, "
|
||||
+ "mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter."
|
||||
));
|
||||
|
||||
TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_RADIATION, null);
|
||||
if (foundInfo != null) {
|
||||
this.setExpansionSetCode(foundInfo.getSetCode());
|
||||
this.setCardNumber("");
|
||||
this.setImageFileName(""); // use default
|
||||
this.setImageNumber(foundInfo.getImageNumber());
|
||||
} else {
|
||||
// how-to fix: add emblem to the tokens-database
|
||||
throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_RADIATION);
|
||||
}
|
||||
}
|
||||
|
||||
private RadiationEmblem(final RadiationEmblem card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RadiationEmblem copy() {
|
||||
return new RadiationEmblem(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum RadiationCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
return player != null && player.getCounters().getCount(CounterType.RAD) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 725.1. Rad counters are a kind of counter a player can have (see rule 122, "Counters").
|
||||
* There is an inherent triggered ability associated with rad counters. This ability has no
|
||||
* source and is controlled by the active player. This is an exception to rule 113.8. The
|
||||
* full text of this ability is "At the beginning of each player's precombat main phase, if
|
||||
* that player has one or more rad counters, that player mills a number of cards equal to
|
||||
* the number of rad counters they have. For each nonland card milled this way, that player
|
||||
* loses 1 life and removes one rad counter from themselves."
|
||||
*/
|
||||
class RadiationEffect extends OneShotEffect {
|
||||
|
||||
RadiationEffect() {
|
||||
super(Outcome.Neutral);
|
||||
staticText = "mill that many cards. For each nonland card milled this way, "
|
||||
+ "you lose 1 life and remove one rad counter.";
|
||||
}
|
||||
|
||||
private RadiationEffect(final RadiationEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RadiationEffect copy() {
|
||||
return new RadiationEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
int amount = player.getCounters().getCount(CounterType.RAD);
|
||||
Cards milled = player.millCards(amount, source, game);
|
||||
int countNonLand = milled.count(StaticFilters.FILTER_CARD_NON_LAND, player.getId(), source, game);
|
||||
if (countNonLand > 0) {
|
||||
// TODO: support gaining life instead with [[Strong, the Brutish Thespian]]
|
||||
player.loseLife(countNonLand, game, source, false);
|
||||
player.removeCounters(CounterType.RAD.getName(), countNonLand, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue