This commit is contained in:
BetaSteward 2010-09-01 03:01:43 +00:00
parent df642c2bd5
commit 3fa0e8b8f4
544 changed files with 13327 additions and 3074 deletions

View file

@ -1,7 +1,10 @@
server-name=localhost
port=17171
remote-server=mage-server
cards-resource-path=resources/images/cards/
symbols-resource-path=resources/images/symbols/
resource-path=resources/images/
cards-resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Cards\\
symbols-resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Chat\\chat_graphic_typingicon_
resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Cards\\Pics\\
#cards-resource-path=resources/images/cards/
#symbols-resource-path=resources/images/symbols/
#resource-path=resources/images/
card-scaling-factor=0.4

View file

@ -20,6 +20,7 @@ debug.test.classpath=\
dist.dir=dist
dist.jar=${dist.dir}/Mage.Client.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
includes=**
jar.compress=false
@ -52,7 +53,7 @@ jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
main.class=mage.client.MageFrame
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
platform.active=default_platform
platform.active=JDK_1.6_21
project.license=bsd
project.Mage=../Mage
project.Mage_Common=../Mage.Common

View file

@ -4,6 +4,7 @@
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>Mage.Client</name>
<explicit-platform explicit-source-supported="true"/>
<source-roots>
<root id="src.dir"/>
</source-roots>

Binary file not shown.

View file

@ -45,7 +45,9 @@ import javax.swing.JLayeredPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import mage.client.dialog.AboutDialog;
import mage.client.dialog.CombatDialog;
import mage.client.dialog.ConnectDialog;
import mage.client.dialog.PickNumberDialog;
import mage.client.remote.Session;
import mage.client.util.EDTExceptionHandler;
import mage.util.Logging;
@ -60,6 +62,8 @@ public class MageFrame extends javax.swing.JFrame {
private static Session session;
private ConnectDialog connectDialog;
private static CombatDialog combat;
private static PickNumberDialog pickNumber;
/**
* @return the session
@ -96,8 +100,12 @@ public class MageFrame extends javax.swing.JFrame {
session = new Session(this);
connectDialog = new ConnectDialog(session);
combat = new CombatDialog();
pickNumber = new PickNumberDialog();
desktopPane.add(connectDialog, JLayeredPane.POPUP_LAYER);
// connectDialog.setLocation(50, 50);
desktopPane.add(combat, JLayeredPane.POPUP_LAYER);
combat.hideDialog();
desktopPane.add(pickNumber, JLayeredPane.POPUP_LAYER);
disableButtons();
}
@ -311,6 +319,14 @@ public class MageFrame extends javax.swing.JFrame {
this.deckEditorPane.setVisible(false);
}
public static CombatDialog getCombatDialog() {
return combat;
}
public static PickNumberDialog getPickNumberDialog() {
return pickNumber;
}
/**
* @param args the command line arguments
*/

View file

@ -63,6 +63,7 @@ import javax.swing.text.StyledDocument;
import mage.Constants.CardType;
import mage.client.MageFrame;
import mage.client.remote.Session;
import mage.client.util.Config;
import mage.client.util.ImageHelper;
import mage.view.CardView;
@ -79,10 +80,11 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
protected Point p;
protected CardDimensions dimension;
protected UUID gameId;
protected BigCard bigCard;
protected final UUID gameId;
protected final BigCard bigCard;
protected CardView card;
protected Popup popup;
protected boolean popupShowing;
protected TextPopup popupText = new TextPopup();
protected BufferedImage background;
@ -97,7 +99,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
this.gameId = gameId;
this.card = card;
this.bigCard = bigCard;
small = new BufferedImage(dimension.frameWidth, dimension.frameHeight, BufferedImage.TYPE_INT_RGB);
small = new BufferedImage(Config.dimensions.frameWidth, Config.dimensions.frameHeight, BufferedImage.TYPE_INT_RGB);
background = ImageHelper.getBackground(card);
StyledDocument doc = text.getStyledDocument();
@ -136,7 +138,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
gSmall.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gSmall.setColor(Color.BLACK);
gSmall.drawImage(ImageHelper.ScaleImage(image, dimension.frameWidth, dimension.frameHeight), 0, 0, this);
gSmall.drawImage(ImageHelper.ScaleImage(image, Config.dimensions.frameWidth, Config.dimensions.frameHeight), 0, 0, this);
gImage.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE));
gImage.drawString(card.getName(), CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET);
@ -152,17 +154,17 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
gImage.dispose();
gSmall.setFont(new Font("Arial", Font.PLAIN, dimension.nameFontSize));
gSmall.drawString(card.getName(), dimension.contentXOffset, dimension.nameYOffset);
gSmall.setFont(new Font("Arial", Font.PLAIN, Config.dimensions.nameFontSize));
gSmall.drawString(card.getName(), Config.dimensions.contentXOffset, Config.dimensions.nameYOffset);
if (card.getCardTypes().contains(CardType.CREATURE)) {
gSmall.drawString(card.getPower() + "/" + card.getToughness(), dimension.powBoxTextLeft, dimension.powBoxTextTop);
gSmall.drawString(card.getPower() + "/" + card.getToughness(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop);
}
else if (card.getCardTypes().contains(CardType.PLANESWALKER)) {
gSmall.drawString(card.getLoyalty(), dimension.powBoxTextLeft, dimension.powBoxTextTop);
gSmall.drawString(card.getLoyalty(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop);
}
if (card.getCardTypes().size() > 0)
gSmall.drawString(cardType, dimension.contentXOffset, dimension.typeYOffset);
gSmall.drawString(cardType, Config.dimensions.contentXOffset, Config.dimensions.typeYOffset);
drawText();
gSmall.dispose();
@ -187,6 +189,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
for (String rule: getRules()) {
sb.append("\n").append(rule);
}
sb.append("\n").append(card.getId());
return sb.toString();
}
@ -272,7 +275,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
} else {
g2.setColor(Color.BLACK);
}
g2.drawRect(0, 0, dimension.frameWidth - 1, dimension.frameHeight - 1);
g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1);
}
@Override
@ -301,21 +304,27 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou
@Override
public void mouseEntered(MouseEvent arg0) {
if (popup != null)
if (!popupShowing) {
if (popup != null)
popup.hide();
PopupFactory factory = PopupFactory.getSharedInstance();
popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + Config.dimensions.frameWidth, (int) this.getLocationOnScreen().getY() + 40);
popup.show();
//hack to get popup to resize to fit text
popup.hide();
PopupFactory factory = PopupFactory.getSharedInstance();
popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + dimension.frameWidth, (int) this.getLocationOnScreen().getY() + 40);
popup.show();
//hack to get popup to resize to fit text
popup.hide();
popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + dimension.frameWidth, (int) this.getLocationOnScreen().getY() + 40);
popup.show();
popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + Config.dimensions.frameWidth, (int) this.getLocationOnScreen().getY() + 40);
popup.show();
popupShowing = true;
}
}
@Override
public void mouseExited(MouseEvent arg0) {
if (popup != null)
if(getMousePosition(true) != null) return;
if (popup != null) {
popup.hide();
popupShowing = false;
}
}
@Override

View file

@ -36,19 +36,19 @@ import static mage.client.util.Constants.*;
*/
public class CardDimensions {
public static int frameHeight;
public static int frameWidth;
public static int symbolHeight;
public static int symbolWidth;
public static int contentXOffset;
public static int nameYOffset;
public static int typeYOffset;
public static int textYOffset;
public static int textWidth;
public static int textHeight;
public static int powBoxTextTop;
public static int powBoxTextLeft;
public static int nameFontSize;
public int frameHeight;
public int frameWidth;
public int symbolHeight;
public int symbolWidth;
public int contentXOffset;
public int nameYOffset;
public int typeYOffset;
public int textYOffset;
public int textWidth;
public int textHeight;
public int powBoxTextTop;
public int powBoxTextLeft;
public int nameFontSize;
public CardDimensions(double scaleFactor) {
frameHeight = (int)(FRAME_MAX_HEIGHT * scaleFactor);

View file

@ -16,12 +16,12 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
<EmptySpace min="0" pref="294" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
<EmptySpace min="0" pref="197" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>

View file

@ -37,12 +37,17 @@ package mage.client.cards;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import mage.client.util.Config;
import mage.client.util.Event;
@ -54,45 +59,52 @@ import mage.view.CardsView;
*
* @author BetaSteward_at_googlemail.com
*/
public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ComponentListener {
public class CardGrid extends javax.swing.JLayeredPane implements MouseListener {
protected CardEventSource cardEventSource = new CardEventSource();
protected BigCard bigCard;
protected UUID gameId;
protected List<Card> cards = new ArrayList<Card>();
private Map<UUID, Card> cards = new HashMap<UUID, Card>();
public CardGrid() {
initComponents();
addComponentListener(this);
setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight));
}
public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) {
this.bigCard = bigCard;
this.gameId = gameId;
cards.clear();
for (CardView card: showCards) {
Card cardImg = new Card(card, bigCard, Config.dimensions, gameId);
cardImg.update(card);
cardImg.addMouseListener(this);
cards.add(cardImg);
for (CardView card: showCards.values()) {
if (!cards.containsKey(card.getId())) {
Card cardImg = new Card(card, bigCard, Config.dimensions, gameId);
cardImg.addMouseListener(this);
add(cardImg);
cardImg.update(card);
cards.put(card.getId(), cardImg);
}
}
for (Iterator<Entry<UUID, Card>> i = cards.entrySet().iterator(); i.hasNext();) {
Entry<UUID, Card> entry = i.next();
if (!showCards.containsKey(entry.getKey())) {
removeCard(entry.getKey());
i.remove();
}
}
drawCards();
this.setVisible(true);
}
public void drawCards() {
removeAll();
int maxWidth = this.getParent().getWidth();
int numColumns = maxWidth / Config.dimensions.frameWidth;
int curColumn = 0;
int curRow = 0;
if (cards.size() > 0) {
Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight);
for (Card cardImg: cards) {
List<Card> sortedCards = new ArrayList<Card>(cards.values());
Collections.sort(sortedCards, new CardComparator());
for (Card cardImg: sortedCards) {
rectangle.setLocation(curColumn * Config.dimensions.frameWidth, curRow * 20);
cardImg.setBounds(rectangle);
add(cardImg);
moveToFront(cardImg);
curColumn++;
if (curColumn == numColumns) {
@ -104,6 +116,17 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
resizeArea();
}
private void removeCard(UUID cardId) {
for (Component comp: getComponents()) {
if (comp instanceof Card) {
if (((Card)comp).getCardId().equals(cardId)) {
remove(comp);
}
}
}
}
public void addCardEventListener(Listener<Event> listener) {
cardEventSource.addListener(listener);
}
@ -125,11 +148,11 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
.addGap(0, 294, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
.addGap(0, 197, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -157,26 +180,6 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void componentResized(ComponentEvent e) {
resizeArea();
}
@Override
public void componentMoved(ComponentEvent e) {
resizeArea();
}
@Override
public void componentShown(ComponentEvent e) {
resizeArea();
}
@Override
public void componentHidden(ComponentEvent e) {
resizeArea();
}
private void resizeArea() {
Dimension area = new Dimension(0, 0);
Dimension size = getPreferredSize();
@ -198,3 +201,12 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
}
}
class CardComparator implements Comparator<Card> {
@Override
public int compare(Card o1, Card o2) {
return o1.card.getName().compareTo(o2.card.getName());
}
}

View file

@ -34,12 +34,16 @@
package mage.client.cards;
import java.awt.Component;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import mage.client.util.Config;
import mage.view.CardView;
import mage.view.CardsView;
import static mage.client.util.Constants.*;
/**
*
@ -47,26 +51,52 @@ import static mage.client.util.Constants.*;
*/
public class Cards extends javax.swing.JPanel {
private Map<UUID, Card> cards = new HashMap<UUID, Card>();
/** Creates new form Cards */
public Cards() {
initComponents();
cardArea.setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight));
}
public void loadCards(CardsView cards, BigCard bigCard, UUID gameId) {
cardArea.removeAll();
for (CardView card: cards) {
Card cardImg = new Card(card, bigCard, Config.dimensions, gameId);
cardArea.add(cardImg);
cardImg.update(card);
public boolean loadCards(CardsView cardsView, BigCard bigCard, UUID gameId) {
boolean changed = false;
for (CardView card: cardsView.values()) {
if (!cards.containsKey(card.getId())) {
Card cardImg = new Card(card, bigCard, Config.dimensions, gameId);
cards.put(card.getId(), cardImg);
cardArea.add(cardImg);
changed = true;
}
cards.get(card.getId()).update(card);
}
for (Iterator<Entry<UUID, Card>> i = cards.entrySet().iterator(); i.hasNext();) {
Entry<UUID, Card> entry = i.next();
if (!cardsView.containsKey(entry.getKey())) {
removeCard(entry.getKey());
i.remove();
changed = true;
}
}
cardArea.setPreferredSize(new Dimension(cards.size() * Config.dimensions.frameWidth, Config.dimensions.frameHeight));
cardArea.revalidate();
cardArea.repaint();
this.revalidate();
this.repaint();
return changed;
}
private void removeCard(UUID cardId) {
for (Component comp: cardArea.getComponents()) {
if (comp instanceof Card) {
if (((Card)comp).getCardId().equals(cardId)) {
cardArea.remove(comp);
}
}
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is

View file

@ -7,6 +7,9 @@
<LineBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>

View file

@ -44,7 +44,6 @@ import mage.client.util.Event;
import mage.client.util.Listener;
import mage.view.CardView;
import mage.view.CardsView;
import static mage.client.util.Constants.*;
/**
*
@ -57,7 +56,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener {
/** Creates new form Cards */
public CardsList() {
initComponents();
cardArea.setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight));
}
public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) {
@ -65,7 +63,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener {
if (showCards != null && showCards.size() > 0) {
Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight);
int count = 0;
for (CardView card: showCards) {
for (CardView card: showCards.values()) {
Card cardImg = new Card(card, bigCard, Config.dimensions, gameId);
cardImg.setBounds(rectangle);
cardArea.add(cardImg);
@ -110,6 +108,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener {
cardArea = new javax.swing.JLayeredPane();
setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight));
setLayout(new java.awt.BorderLayout());
jScrollPane1.setViewportView(cardArea);
@ -131,15 +130,19 @@ public class CardsList extends javax.swing.JPanel implements MouseListener {
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}

View file

@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.swing.PopupFactory;
import mage.client.util.Config;
import mage.client.util.ImageHelper;
import mage.view.CounterView;
import mage.view.PermanentView;
@ -69,7 +70,7 @@ public class Permanent extends Card {
super(permanent, bigCard, dimensions, gameId);
this.setSize(this.getPreferredSize());
this.permanent = permanent;
tappedImage = new BufferedImage(dimension.frameHeight, dimension.frameWidth, BufferedImage.TYPE_INT_RGB);
tappedImage = new BufferedImage(Config.dimensions.frameHeight, Config.dimensions.frameWidth, BufferedImage.TYPE_INT_RGB);
}
public UUID getPermanentId() {
@ -153,10 +154,10 @@ public class Permanent extends Card {
g2.setColor(Color.BLACK);
}
if (permanent.isTapped()) {
g2.drawRect(0, 0, dimension.frameHeight - 1, dimension.frameWidth - 1);
g2.drawRect(0, 0, Config.dimensions.frameHeight - 1, Config.dimensions.frameWidth - 1);
}
else {
g2.drawRect(0, 0, dimension.frameWidth - 1, dimension.frameHeight - 1);
g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1);
}
}
@ -185,10 +186,10 @@ public class Permanent extends Card {
@Override
public Dimension getPreferredSize() {
if (permanent != null && permanent.isTapped()) {
return new Dimension(dimension.frameHeight, dimension.frameWidth);
return new Dimension(Config.dimensions.frameHeight, Config.dimensions.frameWidth);
}
else {
return new Dimension(dimension.frameWidth, dimension.frameHeight);
return new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight);
}
}
@ -203,17 +204,20 @@ public class Permanent extends Card {
@Override
public void mouseEntered(MouseEvent arg0) {
if (popup != null)
if (!popupShowing) {
if (popup != null)
popup.hide();
PopupFactory factory = PopupFactory.getSharedInstance();
int x = (int) this.getLocationOnScreen().getX() + (permanent.isTapped()?Config.dimensions.frameHeight:Config.dimensions.frameWidth);
int y = (int) this.getLocationOnScreen().getY() + 40;
popup = factory.getPopup(this, popupText, x, y);
popup.show();
//hack to get popup to resize to fit text
popup.hide();
PopupFactory factory = PopupFactory.getSharedInstance();
int x = (int) this.getLocationOnScreen().getX() + (permanent.isTapped()?dimension.frameHeight:dimension.frameWidth);
int y = (int) this.getLocationOnScreen().getY() + 40;
popup = factory.getPopup(this, popupText, x, y);
popup.show();
//hack to get popup to resize to fit text
popup.hide();
popup = factory.getPopup(this, popupText, x, y);
popup.show();
popup = factory.getPopup(this, popupText, x, y);
popup.show();
popupShowing = true;
}
}
/** This method is called from within the constructor to

View file

@ -34,12 +34,14 @@
package mage.client.chat;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.util.UUID;
import java.util.logging.Logger;
import mage.client.MageFrame;
import mage.client.remote.Session;
import mage.util.Logging;
import mage.view.ChatMessage.MessageColor;
/**
*
* @author BetaSteward_at_googlemail.com
@ -69,7 +71,24 @@ public class ChatPanel extends javax.swing.JPanel {
session.leaveChat(chatId);
}
public void receiveMessage(String message) {
public void receiveMessage(String message, MessageColor color) {
switch (color) {
case BLACK:
this.txtConversation.setForeground(Color.BLACK);
break;
case RED:
this.txtConversation.setForeground(Color.RED);
break;
case GREEN:
this.txtConversation.setForeground(Color.GREEN);
break;
case BLUE:
this.txtConversation.setForeground(Color.BLUE);
break;
case ORANGE:
this.txtConversation.setForeground(Color.ORANGE);
break;
}
this.txtConversation.append(message + "\n");
txtConversation.setCaretPosition(txtConversation.getText().length() - 1);
}

View file

@ -37,11 +37,13 @@ package mage.client.deckeditor;
import java.awt.Cursor;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.swing.DefaultComboBoxModel;
import mage.Constants.CardType;
import mage.Constants.Zone;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.client.cards.BigCard;
import mage.client.cards.CardGrid;
@ -56,8 +58,9 @@ import mage.view.CardsView;
*/
public class CardSelector extends javax.swing.JPanel implements ComponentListener {
private Cards cards = new CardsImpl(Zone.OUTSIDE);
private FilterCard filter = new FilterCard();
private final Set<Card> allCards = new LinkedHashSet<Card>();
private final List<Card> cards = new ArrayList<Card>();
private final FilterCard filter = new FilterCard();
private BigCard bigCard;
/** Creates new form CardSelector */
@ -68,12 +71,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
public void loadCards(BigCard bigCard) {
this.bigCard = bigCard;
this.cards.clear();
cbExpansionSet.setModel(new DefaultComboBoxModel(Sets.getInstance().toArray()));
cbExpansionSet.insertItemAt("All sets", 0);
cbExpansionSet.setSelectedIndex(0);
for (ExpansionSet set: Sets.getInstance()) {
cards.addAll(set.createCards());
allCards.addAll(set.createCards());
}
filter.setUseColor(true);
filter.getColor().setBlack(true);
@ -98,8 +100,13 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private void filterCards() {
try {
cards.clear();
for (Card card: allCards) {
if (filter.match(card))
cards.add(card);
}
setCursor(new Cursor(Cursor.WAIT_CURSOR));
this.cardGrid.loadCards(new CardsView(cards.getCards(filter)), bigCard, null);
this.cardGrid.loadCards(new CardsView(cards), bigCard, null);
}
finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
@ -110,7 +117,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
return this.cardGrid;
}
public Cards getCards() {
public List<Card> getCards() {
return cards;
}

View file

@ -80,7 +80,12 @@ public class DeckEditorPanel extends javax.swing.JPanel {
@Override
public void event(Event event) {
if (event.getEventName().equals("double-click")) {
deck.getCards().add(createCard(cardSelector.getCards().get((UUID)event.getSource()).getClass()));
for (Card card: cardSelector.getCards()) {
if (card.getId().equals((UUID)event.getSource())) {
deck.getCards().add(createCard(card.getClass()));
break;
}
}
refreshDeck();
}
}
@ -92,7 +97,12 @@ public class DeckEditorPanel extends javax.swing.JPanel {
@Override
public void event(Event event) {
if (event.getEventName().equals("double-click")) {
deck.getCards().remove((UUID)event.getSource());
for (Card card: deck.getCards()) {
if (card.getId().equals((UUID)event.getSource())) {
deck.getCards().remove(card);
break;
}
}
refreshDeck();
}
}
@ -272,8 +282,11 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcSelectDeck.getSelectedFile();
try {
String fileName = file.getPath();
if (!fileName.endsWith(".dck"))
fileName += ".dck";
setCursor(new Cursor(Cursor.WAIT_CURSOR));
deck.getDeckCardLists().save(file.getPath());
deck.getDeckCardLists().save(fileName);
} catch (Exception ex) {
Logger.getLogger(DeckEditorPanel.class.getName()).log(Level.SEVERE, null, ex);
}

View file

@ -33,6 +33,11 @@
</Layout>
<SubComponents>
<Component class="mage.client.cards.Cards" name="cards">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View file

@ -38,9 +38,8 @@ import java.beans.PropertyVetoException;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLayeredPane;
import mage.client.MageFrame;
import mage.client.cards.BigCard;
import mage.client.util.Config;
import mage.view.ExileView;
/**
@ -57,17 +56,21 @@ public class ExileZoneDialog extends MageDialog {
public void loadCards(ExileView exile, BigCard bigCard, UUID gameId) {
this.title = exile.getName();
boolean changed = false;
changed = cards.loadCards(exile, bigCard, gameId);
if (exile.size() > 0) {
cards.loadCards(exile, bigCard, gameId);
if (getParent() != MageFrame.getDesktop() || this.isClosed)
MageFrame.getDesktop().add(this, JLayeredPane.POPUP_LAYER);
// try {
// this.setIcon(false);
// } catch (PropertyVetoException ex) {
// Logger.getLogger(ShowCardsDialog.class.getName()).log(Level.SEVERE, null, ex);
// }
show();
if (changed) {
try {
this.setIcon(false);
} catch (PropertyVetoException ex) {
Logger.getLogger(ExileZoneDialog.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
else {
hide();
}
this.setVisible(exile.size() > 0);
}
/** This method is called from within the constructor to
@ -83,6 +86,8 @@ public class ExileZoneDialog extends MageDialog {
setIconifiable(true);
cards.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(

View file

@ -22,21 +22,26 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace pref="310" max="32767" attributes="0"/>
<Component id="btnOK" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="btnOK" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="newPlayerPanel" alignment="0" pref="414" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="newPlayerPanel" alignment="1" pref="438" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="newPlayerPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="10" max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnOK" alignment="3" min="-2" max="-2" attributes="0"/>

View file

@ -103,17 +103,20 @@ public class JoinTableDialog extends MageDialog {
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(310, Short.MAX_VALUE)
.addComponent(btnOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(btnOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel))
.addComponent(newPlayerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE))
.addContainerGap())
.addComponent(newPlayerPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 438, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(newPlayerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@ -133,7 +136,7 @@ public class JoinTableDialog extends MageDialog {
private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed
Session session = MageFrame.getSession();
try {
joined = session.joinTable(roomId, tableId, 1, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile()));
joined = session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile()));
} catch (Exception ex) {
handleError(ex);
}

View file

@ -24,7 +24,7 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="302" max="32767" attributes="0"/>
<EmptySpace min="0" pref="306" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>

View file

@ -64,6 +64,7 @@ public class MageDialog extends javax.swing.JInternalFrame {
@Override
public void show() {
super.show();
this.toFront();
if (this.modal) {
startModal();
}
@ -72,6 +73,7 @@ public class MageDialog extends javax.swing.JInternalFrame {
@Override
public void setVisible(boolean value) {
super.setVisible(value);
this.toFront();
if (modal) {
if (value) {
startModal();

View file

@ -286,18 +286,16 @@ public class NewTableDialog extends MageDialog {
(MultiplayerAttackOption)this.cbAttackOption.getSelectedItem(),
(RangeOfInfluence)this.cbRange.getSelectedItem());
try {
if (session.joinTable(roomId, table.getTableId(), 0, this.player1Panel.getPlayerName(), DeckCardLists.load(this.player1Panel.getDeckFile()))) {
int seatNum = 1;
if (session.joinTable(roomId, table.getTableId(), this.player1Panel.getPlayerName(), DeckCardLists.load(this.player1Panel.getDeckFile()))) {
for (TablePlayerPanel player: players) {
if (!player.getPlayerType().equals("Human")) {
if (!player.joinTable(roomId, table.getTableId(), seatNum)) {
if (!player.joinTable(roomId, table.getTableId())) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error joining table.", "Error", JOptionPane.ERROR_MESSAGE);
session.removeTable(roomId, table.getTableId());
table = null;
return;
}
}
seatNum++;
}
this.setVisible(false);
return;

View file

@ -20,23 +20,24 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace pref="13" max="32767" attributes="0"/>
<Component id="btnOk" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="btnOk" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" alignment="0" pref="146" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="spnAmount" min="-2" pref="52" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lblMessage" pref="121" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="54" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -44,10 +45,10 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lblMessage" pref="30" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="64" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="spnAmount" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnOk" alignment="3" min="-2" max="-2" attributes="0"/>
@ -81,10 +82,30 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOkActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblMessage">
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
</Properties>
</Component>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="lblMessage">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="editable" type="boolean" value="false"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null&#x9;" type="code"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View file

@ -53,7 +53,9 @@ public class PickNumberDialog extends MageDialog {
public void showDialog(int min, int max, String message) {
this.spnAmount.setModel(new SpinnerNumberModel(min, min, max, 1));
this.lblMessage.setText(message);
this.btnOk.setVisible(true);
this.btnCancel.setVisible(false);
this.pack();
this.setVisible(true);
}
@ -77,7 +79,8 @@ public class PickNumberDialog extends MageDialog {
spnAmount = new javax.swing.JSpinner();
btnCancel = new javax.swing.JButton();
btnOk = new javax.swing.JButton();
lblMessage = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
lblMessage = new javax.swing.JTextPane();
spnAmount.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(1), null, null, Integer.valueOf(1)));
@ -95,35 +98,42 @@ public class PickNumberDialog extends MageDialog {
}
});
lblMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
lblMessage.setBorder(null);
lblMessage.setEditable(false);
lblMessage.setCursor(null );
lblMessage.setFocusable(false);
lblMessage.setOpaque(false);
jScrollPane1.setViewportView(lblMessage);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(13, Short.MAX_VALUE)
.addComponent(btnOk)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel))
.addGroup(layout.createSequentialGroup()
.addGap(44, 44, 44)
.addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(lblMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 121, Short.MAX_VALUE)))
.addContainerGap())
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(btnOk)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE))
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(54, 54, 54))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(lblMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 30, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnCancel)
.addComponent(btnOk))
@ -146,7 +156,8 @@ public class PickNumberDialog extends MageDialog {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnCancel;
private javax.swing.JButton btnOk;
private javax.swing.JLabel lblMessage;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextPane lblMessage;
private javax.swing.JSpinner spnAmount;
// End of variables declaration//GEN-END:variables

View file

@ -78,7 +78,7 @@ public class ShowCardsDialog extends MageDialog implements MouseListener {
private void loadCardsFew(CardsView showCards, BigCard bigCard, CardDimensions dimension, UUID gameId) {
Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight);
for (CardView card: showCards) {
for (CardView card: showCards.values()) {
Card cardImg = new Card(card, bigCard, dimension, gameId);
cardImg.setBounds(rectangle);
cardArea.add(cardImg);
@ -95,7 +95,7 @@ public class ShowCardsDialog extends MageDialog implements MouseListener {
if (showCards != null && showCards.size() > 0) {
Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight);
int count = 0;
for (CardView card: showCards) {
for (CardView card: showCards.values()) {
Card cardImg = new Card(card, bigCard, dimension, gameId);
cardImg.setBounds(rectangle);
cardArea.add(cardImg);

View file

@ -48,7 +48,6 @@ import mage.client.cards.BigCard;
import mage.client.cards.Permanent;
import mage.client.util.Config;
import mage.view.PermanentView;
import static mage.client.util.Constants.*;
/**
*

View file

@ -16,9 +16,9 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblDefender" alignment="0" pref="142" max="32767" attributes="0"/>
<Component id="blockers" alignment="0" pref="142" max="32767" attributes="2"/>
<Component id="attackers" alignment="0" pref="142" max="32767" attributes="2"/>
<Component id="lblDefender" alignment="0" pref="69" max="32767" attributes="0"/>
<Component id="blockers" alignment="0" min="-2" max="-2" attributes="2"/>
<Component id="attackers" alignment="0" min="-2" max="-2" attributes="2"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
@ -26,22 +26,32 @@
<Group type="102" alignment="0" attributes="0">
<Component id="lblDefender" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="blockers" min="-2" pref="199" max="-2" attributes="0"/>
<Component id="blockers" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="attackers" min="-2" pref="199" max="-2" attributes="0"/>
<Component id="attackers" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="mage.client.cards.Cards" name="blockers">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
</Component>
<Component class="mage.client.cards.Cards" name="attackers">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblDefender">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" value="jLabel1"/>
<Property name="text" type="java.lang.String" value="Defender"/>
</Properties>
</Component>
</SubComponents>

View file

@ -39,7 +39,6 @@ import java.util.UUID;
import mage.client.cards.BigCard;
import mage.client.util.Config;
import mage.view.CombatGroupView;
import static mage.client.util.Constants.*;
/**
*
@ -53,7 +52,6 @@ public class CombatGroup extends javax.swing.JPanel {
/** Creates new form CombatGroup */
public CombatGroup() {
initComponents();
// setPreferredSize(new Dimension(160, 440));
}
public void init(UUID gameId, BigCard bigCard) {
@ -64,9 +62,9 @@ public class CombatGroup extends javax.swing.JPanel {
public void update(CombatGroupView combatGroup) {
this.lblDefender.setText(combatGroup.getDefenderName());
this.attackers.loadCards(combatGroup.getAttackers(), bigCard, gameId);
attackers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6));
// attackers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6));
this.blockers.loadCards(combatGroup.getBlockers(), bigCard, gameId);
blockers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6));
// blockers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6));
this.attackers.setVisible(true);
this.blockers.setVisible(true);
}
@ -84,25 +82,29 @@ public class CombatGroup extends javax.swing.JPanel {
attackers = new mage.client.cards.Cards();
lblDefender = new javax.swing.JLabel();
blockers.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25));
attackers.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25));
lblDefender.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
lblDefender.setText("jLabel1");
lblDefender.setText("Defender");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblDefender, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE)
.addComponent(blockers, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE)
.addComponent(attackers, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE)
.addComponent(lblDefender, javax.swing.GroupLayout.DEFAULT_SIZE, 69, Short.MAX_VALUE)
.addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(lblDefender)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents

View file

@ -108,9 +108,10 @@ public class FeedbackPanel extends javax.swing.JPanel {
break;
}
this.btnSpecial.setVisible(special);
this.revalidate();
this.repaint();
if (modal)
startModal();
this.revalidate();
}
public void clear() {

View file

@ -36,6 +36,8 @@ package mage.client.game;
import mage.client.*;
import java.util.UUID;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
/**
*
@ -46,6 +48,33 @@ public class GamePane extends MagePane {
/** Creates new form GamePane */
public GamePane() {
initComponents();
addInternalFrameListener(new InternalFrameListener()
{
@Override
public void internalFrameOpened(InternalFrameEvent e) { }
@Override
public void internalFrameClosing(InternalFrameEvent e) {
gamePanel.cleanUp();
}
@Override
public void internalFrameClosed(InternalFrameEvent e) { }
@Override
public void internalFrameIconified(InternalFrameEvent e) { }
@Override
public void internalFrameDeiconified(InternalFrameEvent e) { }
@Override
public void internalFrameActivated(InternalFrameEvent e) { }
@Override
public void internalFrameDeactivated(InternalFrameEvent e) { }
});
}
public void showGame(UUID gameId, UUID playerId) {

View file

@ -29,7 +29,7 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" pref="847" max="32767" attributes="0"/>
<Component id="jSplitPane1" alignment="0" pref="798" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
@ -62,8 +62,8 @@
<Component id="pnlGameInfo" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pnlHand" max="32767" attributes="0"/>
<Component id="pnlBattlefield" alignment="0" pref="715" max="32767" attributes="0"/>
<Component id="hand" pref="715" max="32767" attributes="0"/>
<Component id="pnlBattlefield" pref="715" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
@ -71,9 +71,9 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="pnlBattlefield" pref="634" max="32767" attributes="0"/>
<Component id="pnlBattlefield" pref="794" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="pnlHand" min="-2" max="-2" attributes="0"/>
<Component id="hand" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="pnlGameInfo" alignment="0" max="32767" attributes="0"/>
</Group>
@ -116,9 +116,7 @@
<Component id="btnConcede" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnStopWatching" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCheat" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="19" max="32767" attributes="0"/>
<EmptySpace pref="62" max="32767" attributes="0"/>
</Group>
<Component id="bigCard" alignment="0" pref="256" max="32767" attributes="0"/>
<Component id="feedbackPanel" alignment="0" pref="256" max="32767" attributes="0"/>
@ -160,16 +158,15 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="feedbackPanel" min="-2" pref="109" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="stack" min="-2" pref="209" max="-2" attributes="0"/>
<Component id="stack" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="bigCard" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="8" max="32767" attributes="0"/>
<EmptySpace pref="164" max="32767" attributes="0"/>
<Component id="pnlReplay" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnConcede" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnStopWatching" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnCheat" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
@ -325,14 +322,11 @@
<Component class="mage.client.cards.BigCard" name="bigCard">
</Component>
<Component class="mage.client.cards.Cards" name="stack">
</Component>
<Component class="javax.swing.JButton" name="btnCheat">
<Properties>
<Property name="text" type="java.lang.String" value="?"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheatActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="pnlReplay">
@ -387,44 +381,17 @@
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnlHand">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hand" alignment="1" pref="711" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hand" alignment="1" pref="209" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="mage.client.cards.Cards" name="hand">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)" type="code"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnlBattlefield">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="mage.client.cards.Cards" name="hand">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)" type="code"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="mage.client.chat.ChatPanel" name="chatPanel">

View file

@ -35,7 +35,6 @@
package mage.client.game;
import java.awt.GridBagConstraints;
import java.util.logging.Level;
import mage.client.*;
import java.util.HashMap;
import java.util.Map;
@ -44,11 +43,8 @@ import java.util.logging.Logger;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import mage.cards.decks.DeckCardLists;
import mage.client.dialog.CombatDialog;
import mage.client.dialog.ExileZoneDialog;
import mage.client.dialog.PickChoiceDialog;
import mage.client.dialog.PickNumberDialog;
import mage.client.dialog.ShowCardsDialog;
import mage.client.game.FeedbackPanel.FeedbackMode;
import mage.client.remote.Session;
@ -73,14 +69,20 @@ public class GamePanel extends javax.swing.JPanel {
private UUID gameId;
private UUID playerId;
private Session session;
private CombatDialog combat = new CombatDialog();
private PickNumberDialog pickNumber = new PickNumberDialog();
/** Creates new form GamePanel */
public GamePanel() {
initComponents();
}
public void cleanUp() {
MageFrame.getCombatDialog().hideDialog();
MageFrame.getPickNumberDialog().hide();
for (ExileZoneDialog exile: exiles.values()) {
exile.hide();
}
}
public synchronized void showGame(UUID gameId, UUID playerId) {
this.gameId = gameId;
this.playerId = playerId;
@ -108,7 +110,6 @@ public class GamePanel extends javax.swing.JPanel {
this.feedbackPanel.clear();
this.btnConcede.setVisible(false);
this.btnStopWatching.setVisible(true);
this.btnCheat.setVisible(false);
this.pnlReplay.setVisible(false);
this.setVisible(true);
this.chatPanel.clear();
@ -124,7 +125,6 @@ public class GamePanel extends javax.swing.JPanel {
this.feedbackPanel.clear();
this.btnConcede.setVisible(false);
this.btnStopWatching.setVisible(false);
this.btnCheat.setVisible(false);
this.pnlReplay.setVisible(true);
this.setVisible(true);
this.chatPanel.clear();
@ -136,16 +136,21 @@ public class GamePanel extends javax.swing.JPanel {
this.chatPanel.disconnect();
this.players.clear();
this.pnlBattlefield.removeAll();
this.combat.hideDialog();
MageFrame.getDesktop().remove(combat);
MageFrame.getCombatDialog().hideDialog();
// MageFrame.getDesktop().remove(combat);
this.setVisible(false);
}
public synchronized void init(GameView game) {
combat.init(gameId, bigCard);
MageFrame.getDesktop().add(combat, JLayeredPane.POPUP_LAYER);
combat.setLocation(500, 300);
MageFrame.getDesktop().add(pickNumber, JLayeredPane.POPUP_LAYER);
MageFrame.getCombatDialog().init(gameId, bigCard);
// MageFrame.getDesktop().add(combat, JLayeredPane.POPUP_LAYER);
MageFrame.getCombatDialog().setLocation(500, 300);
// MageFrame.getDesktop().add(pickNumber, JLayeredPane.POPUP_LAYER);
addPlayers(game);
updateGame(game);
}
private void addPlayers(GameView game) {
this.players.clear();
this.pnlBattlefield.removeAll();
//arrange players in a circle with the session player at the bottom left
@ -155,10 +160,12 @@ public class GamePanel extends javax.swing.JPanel {
int col = 0;
int row = 1;
int playerSeat = 0;
for (PlayerView player: game.getPlayers()) {
if (playerId.equals(player.getPlayerId()))
break;
playerSeat++;
if (playerId != null) {
for (PlayerView player: game.getPlayers()) {
if (playerId.equals(player.getPlayerId()))
break;
playerSeat++;
}
}
PlayerView player = game.getPlayers().get(playerSeat);
PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId);
@ -176,6 +183,8 @@ public class GamePanel extends javax.swing.JPanel {
if (oddNumber)
col++;
int playerNum = playerSeat + 1;
if (playerNum >= numSeats)
playerNum = 0;
while (true) {
if (row == 1)
col++;
@ -202,7 +211,6 @@ public class GamePanel extends javax.swing.JPanel {
if (playerNum == playerSeat)
break;
}
updateGame(game);
}
public synchronized void updateGame(GameView game) {
@ -229,20 +237,23 @@ public class GamePanel extends javax.swing.JPanel {
if (!exiles.containsKey(exile.getId())) {
ExileZoneDialog newExile = new ExileZoneDialog();
exiles.put(exile.getId(), newExile);
MageFrame.getDesktop().add(newExile, JLayeredPane.POPUP_LAYER);
newExile.show();
}
exiles.get(exile.getId()).loadCards(exile, bigCard, gameId);
}
if (game.getCombat().size() > 0) {
combat.showDialog(game.getCombat());
MageFrame.getCombatDialog().showDialog(game.getCombat());
}
else {
combat.hideDialog();
MageFrame.getCombatDialog().hideDialog();
}
this.revalidate();
this.repaint();
}
public void ask(String question) {
public void ask(String question, GameView gameView) {
updateGame(gameView);
this.feedbackPanel.getFeedback(FeedbackMode.QUESTION, question, true, false);
}
@ -309,11 +320,11 @@ public class GamePanel extends javax.swing.JPanel {
}
public void getAmount(int min, int max, String message) {
pickNumber.showDialog(min, max, message);
if (pickNumber.isCancel())
MageFrame.getPickNumberDialog().showDialog(min, max, message);
if (MageFrame.getPickNumberDialog().isCancel())
session.sendPlayerBoolean(gameId, false);
else
session.sendPlayerInteger(gameId, pickNumber.getAmount());
session.sendPlayerInteger(gameId, MageFrame.getPickNumberDialog().getAmount());
}
public void getChoice(String message, String[] choices) {
@ -350,14 +361,12 @@ public class GamePanel extends javax.swing.JPanel {
btnStopWatching = new javax.swing.JButton();
bigCard = new mage.client.cards.BigCard();
stack = new mage.client.cards.Cards();
btnCheat = new javax.swing.JButton();
pnlReplay = new javax.swing.JPanel();
btnStopReplay = new javax.swing.JButton();
btnPreviousPlay = new javax.swing.JButton();
btnNextPlay = new javax.swing.JButton();
pnlHand = new javax.swing.JPanel();
hand = new mage.client.cards.Cards();
pnlBattlefield = new javax.swing.JPanel();
hand = new mage.client.cards.Cards();
chatPanel = new mage.client.chat.ChatPanel();
jSplitPane1.setBorder(null);
@ -420,12 +429,7 @@ public class GamePanel extends javax.swing.JPanel {
}
});
btnCheat.setText("?");
btnCheat.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCheatActionPerformed(evt);
}
});
stack.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25));
btnStopReplay.setText("Stop");
btnStopReplay.addActionListener(new java.awt.event.ActionListener() {
@ -492,9 +496,7 @@ public class GamePanel extends javax.swing.JPanel {
.addComponent(btnConcede)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnStopWatching)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCheat)
.addContainerGap(19, Short.MAX_VALUE))
.addContainerGap(62, Short.MAX_VALUE))
.addComponent(bigCard, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE)
.addComponent(feedbackPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE)
.addComponent(stack, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE)
@ -528,36 +530,21 @@ public class GamePanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(feedbackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(stack, javax.swing.GroupLayout.PREFERRED_SIZE, 209, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(stack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 164, Short.MAX_VALUE)
.addComponent(pnlReplay, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(pnlGameInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnConcede)
.addComponent(btnStopWatching)
.addComponent(btnCheat)))
);
pnlHand.setBorder(javax.swing.BorderFactory.createEtchedBorder());
pnlHand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25));
hand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight));
javax.swing.GroupLayout pnlHandLayout = new javax.swing.GroupLayout(pnlHand);
pnlHand.setLayout(pnlHandLayout);
pnlHandLayout.setHorizontalGroup(
pnlHandLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hand, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 711, Short.MAX_VALUE)
);
pnlHandLayout.setVerticalGroup(
pnlHandLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hand, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)
.addComponent(btnStopWatching)))
);
pnlBattlefield.setLayout(new java.awt.GridBagLayout());
hand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25));
javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
jPanel3.setLayout(jPanel3Layout);
jPanel3Layout.setHorizontalGroup(
@ -566,15 +553,15 @@ public class GamePanel extends javax.swing.JPanel {
.addComponent(pnlGameInfo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pnlHand, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(hand, javax.swing.GroupLayout.DEFAULT_SIZE, 715, Short.MAX_VALUE)
.addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 715, Short.MAX_VALUE)))
);
jPanel3Layout.setVerticalGroup(
jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup()
.addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 634, Short.MAX_VALUE)
.addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 794, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addComponent(pnlHand, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(hand, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(pnlGameInfo, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
@ -591,7 +578,7 @@ public class GamePanel extends javax.swing.JPanel {
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 847, Short.MAX_VALUE)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 798, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -621,18 +608,9 @@ public class GamePanel extends javax.swing.JPanel {
session.previousPlay();
}//GEN-LAST:event_btnPreviousPlayActionPerformed
private void btnCheatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheatActionPerformed
try {
session.cheat(gameId, DeckCardLists.load("cheat.dck"));
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
}//GEN-LAST:event_btnCheatActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private mage.client.game.AbilityPicker abilityPicker;
private mage.client.cards.BigCard bigCard;
private javax.swing.JButton btnCheat;
private javax.swing.JButton btnConcede;
private javax.swing.JButton btnNextPlay;
private javax.swing.JButton btnPreviousPlay;
@ -650,7 +628,6 @@ public class GamePanel extends javax.swing.JPanel {
private javax.swing.JLabel lblTurn;
private javax.swing.JPanel pnlBattlefield;
private javax.swing.JPanel pnlGameInfo;
private javax.swing.JPanel pnlHand;
private javax.swing.JPanel pnlReplay;
private mage.client.cards.Cards stack;
private javax.swing.JLabel txtActivePlayer;

View file

@ -25,8 +25,8 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="1" pref="278" max="32767" attributes="0"/>
<Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
<Component id="jScrollPane1" alignment="1" pref="252" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
@ -45,6 +45,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="manaPool" alignment="0" pref="116" max="32767" attributes="1"/>
<Component id="playerPanel" alignment="0" max="32767" attributes="1"/>
<Component id="btnCheat" alignment="0" pref="116" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
@ -53,7 +54,8 @@
<Component id="playerPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="manaPool" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="14" max="32767" attributes="0"/>
<EmptySpace pref="17" max="32767" attributes="0"/>
<Component id="btnCheat" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -63,6 +65,14 @@
</Component>
<Component class="mage.client.game.ManaPool" name="manaPool">
</Component>
<Component class="javax.swing.JButton" name="btnCheat">
<Properties>
<Property name="text" type="java.lang.String" value="Cheat"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheatActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">

View file

@ -34,7 +34,13 @@
package mage.client.game;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.cards.decks.DeckCardLists;
import mage.client.MageFrame;
import mage.client.cards.BigCard;
import mage.view.PlayerView;
@ -44,7 +50,9 @@ import mage.view.PlayerView;
*/
public class PlayAreaPanel extends javax.swing.JPanel {
UUID playerId;
UUID gameId;
/** Creates new form PlayAreaPanel */
public PlayAreaPanel() {
initComponents();
@ -59,6 +67,14 @@ public class PlayAreaPanel extends javax.swing.JPanel {
public void init(PlayerView player, BigCard bigCard, UUID gameId) {
this.playerPanel.init(gameId, player.getPlayerId(), bigCard);
this.battlefieldPanel.init(gameId, bigCard);
if (MageFrame.getSession().isTestMode()) {
this.playerId = player.getPlayerId();
this.gameId = gameId;
this.btnCheat.setVisible(true);
}
else {
this.btnCheat.setVisible(false);
}
}
public void update(PlayerView player) {
@ -80,17 +96,26 @@ public class PlayAreaPanel extends javax.swing.JPanel {
jPanel1 = new javax.swing.JPanel();
playerPanel = new mage.client.game.PlayerPanel();
manaPool = new mage.client.game.ManaPool();
btnCheat = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
battlefieldPanel = new mage.client.game.BattlefieldPanel();
jPanel1.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
btnCheat.setText("Cheat");
btnCheat.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCheatActionPerformed(evt);
}
});
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(manaPool, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE)
.addComponent(playerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btnCheat, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -98,7 +123,8 @@ public class PlayAreaPanel extends javax.swing.JPanel {
.addComponent(playerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(manaPool, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(14, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 17, Short.MAX_VALUE)
.addComponent(btnCheat))
);
jScrollPane1.setViewportView(battlefieldPanel);
@ -114,14 +140,27 @@ public class PlayAreaPanel extends javax.swing.JPanel {
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 252, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void btnCheatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheatActionPerformed
try {
MageFrame.getSession().cheat(gameId, playerId, DeckCardLists.load("cheat.dck"));
} catch (FileNotFoundException ex) {
Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}//GEN-LAST:event_btnCheatActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private mage.client.game.BattlefieldPanel battlefieldPanel;
private javax.swing.JButton btnCheat;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private mage.client.game.ManaPool manaPool;

View file

@ -65,91 +65,90 @@ public class Client implements CallbackClient {
@Override
public synchronized void processCallback(ClientCallback callback) {
if (callback.getMessageId() > messageId) {
messageId = callback.getMessageId();
logger.info(callback.getMessageId() + " - " + callback.getMethod());
if (callback.getMethod().equals("startGame")) {
UUID[] data = (UUID[]) callback.getData();
gameStarted(data[0], data[1]);
logger.info(callback.getMessageId() + " - " + callback.getMethod());
if (callback.getMethod().equals("startGame")) {
UUID[] data = (UUID[]) callback.getData();
gameStarted(data[0], data[1]);
}
else if (callback.getMethod().equals("replayGame")) {
replayGame();
}
else if (callback.getMethod().equals("watchGame")) {
watchGame((UUID) callback.getData());
}
else if (callback.getMethod().equals("chatMessage")) {
ChatMessage message = (ChatMessage) callback.getData();
session.getChats().get(message.getChatId()).receiveMessage(message.getMessage(), message.getColor());
}
else if (callback.getMethod().equals("replayInit")) {
session.getGame().init((GameView) callback.getData());
}
else if (callback.getMethod().equals("replayDone")) {
session.getGame().modalMessage((String) callback.getData());
session.getGame().hideGame();
}
else if (callback.getMethod().equals("replayUpdate")) {
session.getGame().updateGame((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameInit")) {
session.getGame().init((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameOver")) {
session.getGame().modalMessage((String) callback.getData());
session.getGame().hideGame();
}
else if (callback.getMethod().equals("gameAsk")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().ask(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gameTarget")) {
GameClientMessage message = (GameClientMessage) callback.getData();
if (message.isFlag()) {
session.getGame().inform(message.getMessage(), message.getCardsView(), message.getGameView());
} else {
session.getGame().cancel(message.getMessage(), message.getCardsView(), message.getGameView());
}
else if (callback.getMethod().equals("replayGame")) {
replayGame();
}
else if (callback.getMethod().equals("watchGame")) {
watchGame((UUID) callback.getData());
}
else if (callback.getMethod().equals("chatMessage")) {
ChatMessage message = (ChatMessage) callback.getData();
session.getChats().get(message.getChatId()).receiveMessage(message.getMessage());
}
else if (callback.getMethod().equals("replayInit")) {
session.getGame().init((GameView) callback.getData());
}
else if (callback.getMethod().equals("replayDone")) {
session.getGame().modalMessage((String) callback.getData());
session.getGame().hideGame();
}
else if (callback.getMethod().equals("replayUpdate")) {
session.getGame().updateGame((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameInit")) {
session.getGame().init((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameUpdate")) {
session.getGame().updateGame((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameInform")) {
}
else if (callback.getMethod().equals("gameSelect")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().select(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gameChooseAbility")) {
session.getGame().pickAbility((AbilityPickerView) callback.getData());
}
else if (callback.getMethod().equals("gameChoose")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().getChoice(message.getMessage(), message.getStrings());
}
else if (callback.getMethod().equals("gamePlayMana")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().playMana(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gamePlayXMana")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().playXMana(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gameSelectAmount")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().getAmount(message.getMin(), message.getMax(), message.getMessage());
}
else if (callback.getMethod().equals("gameReveal")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().revealCards(message.getMessage(), message.getCardsView());
}
else if (callback.getMethod().equals("gameUpdate")) {
session.getGame().updateGame((GameView) callback.getData());
}
else if (callback.getMethod().equals("gameInform")) {
if (callback.getMessageId() > messageId) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().inform(message.getMessage(), null, message.getGameView());
}
else if (callback.getMethod().equals("gameOver")) {
session.getGame().modalMessage((String) callback.getData());
session.getGame().hideGame();
}
else if (callback.getMethod().equals("gameAsk")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().updateGame(message.getGameView());
session.getGame().ask(message.getMessage());
}
else if (callback.getMethod().equals("gameTarget")) {
GameClientMessage message = (GameClientMessage) callback.getData();
if (message.isFlag()) {
session.getGame().inform(message.getMessage(), message.getCardsView(), message.getGameView());
} else {
session.getGame().cancel(message.getMessage(), message.getCardsView(), message.getGameView());
}
}
else if (callback.getMethod().equals("gameSelect")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().select(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gameChooseAbility")) {
session.getGame().pickAbility((AbilityPickerView) callback.getData());
}
else if (callback.getMethod().equals("gameChoose")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().getChoice(message.getMessage(), message.getStrings());
}
else if (callback.getMethod().equals("gamePlayMana")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().playMana(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gamePlayXMana")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().playXMana(message.getMessage(), message.getGameView());
}
else if (callback.getMethod().equals("gameSelectAmount")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().getAmount(message.getMin(), message.getMax(), message.getMessage());
}
else if (callback.getMethod().equals("gameReveal")) {
GameClientMessage message = (GameClientMessage) callback.getData();
session.getGame().revealCards(message.getMessage(), message.getCardsView());
else {
logger.warning("message out of sequence - ignoring");
}
}
else {
logger.warning("message out of sequence - ignoring");
}
messageId = callback.getMessageId();
}
public UUID getId() throws RemoteException {

View file

@ -50,6 +50,7 @@ import mage.client.util.Config;
import mage.game.GameException;
import mage.interfaces.MageException;
import mage.interfaces.Server;
import mage.interfaces.ServerState;
import mage.interfaces.callback.CallbackClientDaemon;
import mage.util.Logging;
import mage.view.GameTypeView;
@ -68,9 +69,10 @@ public class Session {
private Client client;
private String userName;
private MageFrame frame;
private String[] playerTypes;
private List<GameTypeView> gameTypes;
private String[] deckTypes;
private ServerState serverState;
// private String[] playerTypes;
// private List<GameTypeView> gameTypes;
// private String[] deckTypes;
private Map<UUID, ChatPanel> chats = new HashMap<UUID, ChatPanel>();
private GamePanel game;
private CallbackClientDaemon callbackDaemon;
@ -91,9 +93,7 @@ public class Session {
this.client = new Client(this, frame, userName);
sessionId = server.registerClient(userName, client.getId());
callbackDaemon = new CallbackClientDaemon(sessionId, client, server);
playerTypes = server.getPlayerTypes();
gameTypes = server.getGameTypes();
deckTypes = server.getDeckTypes();
serverState = server.getServerState();
logger.info("Connected to RMI server at " + serverName + ":" + port);
frame.setStatusText("Connected to " + serverName + ":" + port + " ");
frame.enableButtons();
@ -127,15 +127,19 @@ public class Session {
}
public String[] getPlayerTypes() {
return playerTypes;
return serverState.getPlayerTypes();
}
public List<GameTypeView> getGameTypes() {
return gameTypes;
return serverState.getGameTypes();
}
public String[] getDeckTypes() {
return deckTypes;
return serverState.getDeckTypes();
}
public boolean isTestMode() {
return serverState.isTestMode();
}
public Map<UUID, ChatPanel> getChats() {
@ -229,9 +233,9 @@ public class Session {
return false;
}
public boolean joinTable(UUID roomId, UUID tableId, int seat, String playerName, DeckCardLists deckList) {
public boolean joinTable(UUID roomId, UUID tableId, String playerName, DeckCardLists deckList) {
try {
return server.joinTable(sessionId, roomId, tableId, seat, playerName, deckList);
return server.joinTable(sessionId, roomId, tableId, playerName, deckList);
} catch (RemoteException ex) {
handleRemoteException(ex);
} catch (MageException ex) {
@ -506,9 +510,9 @@ public class Session {
return false;
}
public boolean cheat(UUID gameId, DeckCardLists deckList) {
public boolean cheat(UUID gameId, UUID playerId, DeckCardLists deckList) {
try {
server.cheat(gameId, sessionId, deckList);
server.cheat(gameId, sessionId, playerId, deckList);
return true;
} catch (RemoteException ex) {
handleRemoteException(ex);

View file

@ -72,9 +72,9 @@ public class TablePlayerPanel extends javax.swing.JPanel {
this.lblPlayerNum.setText("Player " + playerNum);
}
public boolean joinTable(UUID roomId, UUID tableId, int seatNum) throws FileNotFoundException, IOException, ClassNotFoundException {
public boolean joinTable(UUID roomId, UUID tableId) throws FileNotFoundException, IOException, ClassNotFoundException {
if (!this.cbPlayerType.getSelectedItem().equals("Human")) {
return session.joinTable(roomId, tableId, seatNum, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile()));
return session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile()));
}
return true;
}

View file

@ -266,14 +266,12 @@ public class TablesPanel extends javax.swing.JPanel implements Observer {
session.joinTable(
roomId,
table.getTableId(),
0,
"Human",
DeckCardLists.load("test.dck")
);
session.joinTable(
roomId,
table.getTableId(),
1,
"Computer",
DeckCardLists.load("test.dck")
);

View file

@ -46,17 +46,14 @@ import mage.view.TableView;
*/
public interface Server extends Remote, CallbackServer {
// public String getClientIp() throws RemoteException, MageException;
public UUID registerClient(String userName, UUID clientId) throws RemoteException, MageException;
public void deregisterClient(UUID sessionId) throws RemoteException, MageException;
public List<GameTypeView> getGameTypes() throws RemoteException, MageException;
public String[] getPlayerTypes() throws RemoteException, MageException;
public String[] getDeckTypes() throws RemoteException, MageException;
public ServerState getServerState() throws RemoteException, MageException;
//table methods
public TableView createTable(UUID sessionId, UUID roomId, String gameType, String deckType, List<String> playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) throws RemoteException, MageException;
public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException;
public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException;
public boolean watchTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException;
public boolean replayTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException;
public void leaveTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException;
@ -95,6 +92,6 @@ public interface Server extends Remote, CallbackServer {
public void previousPlay(UUID sessionId) throws RemoteException, MageException;
//test methods
public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) throws RemoteException, MageException;
public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) throws RemoteException, MageException;
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.interfaces;
import java.io.Serializable;
import java.util.List;
import mage.view.GameTypeView;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ServerState implements Serializable {
private List<GameTypeView> gameTypes;
private String[] playerTypes;
private String[] deckTypes;
private boolean testMode;
public ServerState(List<GameTypeView> gameTypes, String[] playerTypes, String[] deckTypes, boolean testMode) {
this.gameTypes = gameTypes;
this.playerTypes = playerTypes;
this.deckTypes = deckTypes;
this.testMode = testMode;
}
public List<GameTypeView> getGameTypes() {
return gameTypes;
}
public String[] getPlayerTypes() {
return playerTypes;
}
public String[] getDeckTypes() {
return deckTypes;
}
public boolean isTestMode() {
return testMode;
}
}

View file

@ -78,7 +78,7 @@ public class CardView implements Serializable {
}
protected CardView() {
}
protected List<String> formatRules(List<String> rules) {

View file

@ -28,40 +28,39 @@
package mage.view;
import java.util.ArrayList;
import java.util.Collection;
import mage.MageObject;
import java.util.HashMap;
import java.util.UUID;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.cards.Cards;
import mage.game.Game;
import mage.game.GameState;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class CardsView extends ArrayList<CardView> {
public class CardsView extends HashMap<UUID, CardView> {
public CardsView() {}
public CardsView(Collection<Card> cards) {
for (Card card: cards) {
this.add(new CardView(card));
this.put(card.getId(), new CardView(card));
}
}
public CardsView(Cards cards) {
if (cards != null)
for (Card card: cards.values()) {
this.add(new CardView(card));
}
}
// public CardsView(Cards cards) {
// if (cards != null)
// for (Card card: cards.values()) {
// this.add(new CardView(card));
// }
// }
public CardsView(Collection<? extends Ability> abilities, Game game) {
public CardsView(Collection<? extends Ability> abilities, GameState state) {
for (Ability ability: abilities) {
String sourceName = game.getPermanent(ability.getSourceId()).getName();
this.add(new AbilityView(ability, sourceName));
String sourceName = state.getPermanent(ability.getSourceId()).getName();
this.put(ability.getId(), new AbilityView(ability, sourceName));
}
}

View file

@ -39,10 +39,16 @@ public class ChatMessage implements Serializable {
private UUID chatId;
private String message;
private MessageColor color;
public ChatMessage(UUID chatId, String message) {
public enum MessageColor {
BLACK, RED, GREEN, BLUE, ORANGE;
}
public ChatMessage(UUID chatId, String message, MessageColor color) {
this.chatId = chatId;
this.message = message;
this.color = color;
}
public String getMessage() {
@ -52,4 +58,8 @@ public class ChatMessage implements Serializable {
public UUID getChatId() {
return chatId;
}
public MessageColor getColor() {
return color;
}
}

View file

@ -30,6 +30,7 @@ package mage.view;
import java.io.Serializable;
import java.util.UUID;
import mage.game.Game;
import mage.game.GameState;
import mage.game.combat.CombatGroup;
import mage.game.permanent.Permanent;
@ -45,20 +46,24 @@ public class CombatGroupView implements Serializable {
private CardsView blockers = new CardsView();
private String defenderName;
public CombatGroupView(CombatGroup combatGroup, GameState game) {
Player player = game.getPlayer(combatGroup.getDefenderId());
public CombatGroupView(CombatGroup combatGroup, GameState state) {
Player player = state.getPlayer(combatGroup.getDefenderId());
if (player != null) {
this.defenderName = player.getName();
}
else {
Permanent perm = game.getPermanent(combatGroup.getDefenderId());
Permanent perm = state.getPermanent(combatGroup.getDefenderId());
this.defenderName = perm.getName();
}
for (UUID id: combatGroup.getAttackers()) {
attackers.add(new PermanentView(game.getPermanent(id)));
Permanent attacker = state.getPermanent(id);
if (attacker != null)
attackers.put(id, new PermanentView(attacker));
}
for (UUID id: combatGroup.getBlockers()) {
blockers.add(new PermanentView(game.getPermanent(id)));
Permanent blocker = state.getPermanent(id);
if (blocker != null)
blockers.put(id, new PermanentView(blocker));
}
}

View file

@ -31,6 +31,7 @@ package mage.view;
import java.util.UUID;
import mage.cards.Card;
import mage.game.ExileZone;
import mage.game.Game;
/**
*
@ -41,11 +42,11 @@ public class ExileView extends CardsView {
private String name;
private UUID id;
public ExileView(ExileZone exileZone) {
public ExileView(ExileZone exileZone, Game game) {
this.name = exileZone.getName();
this.id = exileZone.getId();
for (Card card: exileZone.values()) {
this.add(new CardView(card));
for (Card card: exileZone.getCards(game)) {
this.put(card.getId(), new CardView(card));
}
}

View file

@ -35,6 +35,7 @@ import mage.Constants.PhaseStep;
import mage.Constants.TurnPhase;
import mage.MageObject;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.GameState;
import mage.game.combat.CombatGroup;
import mage.game.stack.Spell;
@ -60,40 +61,40 @@ public class GameView implements Serializable {
private int turn;
private boolean special = false;
public GameView(GameState game) {
for (Player player: game.getPlayers().values()) {
players.add(new PlayerView(player, game));
public GameView(GameState state, Game game) {
for (Player player: state.getPlayers().values()) {
players.add(new PlayerView(player, state, game));
}
for (StackObject stackObject: game.getStack()) {
for (StackObject stackObject: state.getStack()) {
if (stackObject instanceof StackAbility) {
MageObject object = game.getObject(stackObject.getSourceId());
if (object != null)
stack.add(new StackAbilityView((StackAbility)stackObject, object.getName()));
stack.put(stackObject.getId(), new StackAbilityView((StackAbility)stackObject, object.getName()));
else
stack.add(new StackAbilityView((StackAbility)stackObject, ""));
stack.put(null, new StackAbilityView((StackAbility)stackObject, ""));
}
else {
stack.add(new CardView((Spell)stackObject));
stack.put(stackObject.getId(), new CardView((Spell)stackObject));
}
}
for (ExileZone exileZone: game.getExile().getExileZones()) {
exiles.add(new ExileView(exileZone));
for (ExileZone exileZone: state.getExile().getExileZones()) {
exiles.add(new ExileView(exileZone, game));
}
this.phase = game.getTurn().getPhaseType();
this.step = game.getTurn().getStepType();
this.turn = game.getTurnNum();
if (game.getActivePlayerId() != null)
this.activePlayerName = game.getPlayer(game.getActivePlayerId()).getName();
this.phase = state.getTurn().getPhaseType();
this.step = state.getTurn().getStepType();
this.turn = state.getTurnNum();
if (state.getActivePlayerId() != null)
this.activePlayerName = state.getPlayer(state.getActivePlayerId()).getName();
else
this.activePlayerName = "";
if (game.getPriorityPlayerId() != null)
this.priorityPlayerName = game.getPlayer(game.getPriorityPlayerId()).getName();
if (state.getPriorityPlayerId() != null)
this.priorityPlayerName = state.getPlayer(state.getPriorityPlayerId()).getName();
else
this.priorityPlayerName = "";
for (CombatGroup combatGroup: game.getCombat().getGroups()) {
combat.add(new CombatGroupView(combatGroup, game));
for (CombatGroup combatGroup: state.getCombat().getGroups()) {
combat.add(new CombatGroupView(combatGroup, state));
}
this.special = game.getSpecialActions().getControlledBy(game.getPriorityPlayerId()).size() > 0;
this.special = state.getSpecialActions().getControlledBy(state.getPriorityPlayerId()).size() > 0;
}
public List<PlayerView> getPlayers() {

View file

@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.cards.Card;
import mage.game.Game;
import mage.game.GameState;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -54,33 +55,33 @@ public class PlayerView implements Serializable {
private CardsView graveyard = new CardsView();
private Map<UUID, PermanentView> battlefield = new HashMap<UUID, PermanentView>();
public PlayerView(Player player, GameState game) {
public PlayerView(Player player, GameState state, Game game) {
this.playerId = player.getId();
this.name = player.getName();
this.life = player.getLife();
this.libraryCount = player.getLibrary().size();
this.handCount = player.getHand().size();
this.manaPool = new ManaPoolView(player.getManaPool());
this.isActive = (player.getId().equals(game.getActivePlayerId()));
this.isActive = (player.getId().equals(state.getActivePlayerId()));
this.hasLeft = player.hasLeft();
for (Card card: player.getGraveyard().values()) {
graveyard.add(new CardView(card));
for (Card card: player.getGraveyard().getCards(game)) {
graveyard.put(card.getId(), new CardView(card));
}
for (Permanent permanent: game.getBattlefield().getAllPermanents()) {
if (showInBattlefield(permanent, game)) {
for (Permanent permanent: state.getBattlefield().getAllPermanents()) {
if (showInBattlefield(permanent, state)) {
PermanentView view = new PermanentView(permanent);
battlefield.put(view.getId(), view);
}
}
}
private boolean showInBattlefield(Permanent permanent, GameState game) {
private boolean showInBattlefield(Permanent permanent, GameState state) {
//show permanents controlled by player or attachments to permanents controlled by player
if (permanent.getAttachedTo() == null)
return permanent.getControllerId().equals(playerId);
else
return game.getPermanent(permanent.getAttachedTo()).getControllerId().equals(playerId);
return state.getPermanent(permanent.getAttachedTo()).getControllerId().equals(playerId);
}
public int getLife() {

View file

@ -58,8 +58,8 @@ public class Constructed extends DeckValidatorImpl {
List<String> basicLandNames = new ArrayList<String>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains"));
Map<String, Integer> counts = new HashMap<String, Integer>();
countCards(counts, deck.getCards().values());
countCards(counts, deck.getSideboard().values());
countCards(counts, deck.getCards());
countCards(counts, deck.getSideboard());
for (Entry<String, Integer> entry: counts.entrySet()) {
if (entry.getValue() > 4) {
if (!basicLandNames.contains(entry.getKey())) {

View file

@ -41,7 +41,7 @@ import mage.players.Player;
*
* @author BetaSteward_at_googlemail.com
*/
public class FreeForAll extends GameImpl {
public class FreeForAll extends GameImpl<FreeForAll> {
private int numPlayers;
private List<UUID> mulliganed = new ArrayList<UUID>();
@ -50,6 +50,14 @@ public class FreeForAll extends GameImpl {
super(attackOption, range);
}
public FreeForAll(final FreeForAll game) {
super(game);
this.numPlayers = game.numPlayers;
for (UUID playerId: game.mulliganed) {
mulliganed.add(playerId);
}
}
@Override
public GameType getGameType() {
return new FreeForAllType();
@ -88,11 +96,16 @@ public class FreeForAll extends GameImpl {
numCards += 1;
mulliganed.add(playerId);
}
player.getLibrary().addAll(player.getHand());
player.getLibrary().addAll(player.getHand().getCards(this));
player.getHand().clear();
player.shuffleLibrary(this);
player.drawCards(numCards - 1, this);
fireInformEvent(player.getName() + " mulligans down to " + Integer.toString(numCards - 1) + " cards");
}
@Override
public FreeForAll copy() {
return new FreeForAll(this);
}
}

View file

@ -36,12 +36,16 @@ import mage.Constants.PhaseStep;
import mage.Constants.RangeOfInfluence;
import mage.game.turn.TurnMod;
public class TwoPlayerDuel extends GameImpl {
public class TwoPlayerDuel extends GameImpl<TwoPlayerDuel> {
public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range) {
super(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL);
}
public TwoPlayerDuel(final TwoPlayerDuel game) {
super(game);
}
@Override
public GameType getGameType() {
return new TwoPlayerDuelType();
@ -88,4 +92,9 @@ public class TwoPlayerDuel extends GameImpl {
return opponents;
}
@Override
public TwoPlayerDuel copy() {
return new TwoPlayerDuel(this);
}
}

View file

@ -20,6 +20,7 @@ debug.test.classpath=\
dist.dir=dist
dist.jar=${dist.dir}/Mage.Player.AI.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.Mage.AI-src=src
file.reference.Mage.AI-test=test

View file

@ -40,14 +40,18 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.Constants.CardType;
import mage.Constants.Outcome;
import mage.Constants.RangeOfInfluence;
import mage.Constants.Zone;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.TriggeredAbilities;
import mage.abilities.TriggeredAbility;
@ -103,10 +107,10 @@ import mage.util.TreeNode;
*
* @author BetaSteward_at_googlemail.com
*/
public class ComputerPlayer extends PlayerImpl implements Player {
public class ComputerPlayer<T extends ComputerPlayer<T>> extends PlayerImpl<T> implements Player {
private final static transient Logger logger = Logging.getLogger(ComputerPlayer.class.getName());
private boolean abort = false;
private boolean abort;
private transient Map<Mana, Card> unplayable = new TreeMap<Mana, Card>();
private transient List<Card> playableNonInstant = new ArrayList<Card>();
private transient List<Card> playableInstant = new ArrayList<Card>();
@ -121,31 +125,37 @@ public class ComputerPlayer extends PlayerImpl implements Player {
super(id);
}
public ComputerPlayer(final ComputerPlayer player) {
super(player);
this.abort = player.abort;
}
@Override
public boolean chooseMulligan(Game game) {
logger.fine("chooseMulligan");
if (hand.size() < 6)
return false;
List<Card> lands = hand.getCards(new FilterLandCard());
Set<Card> lands = hand.getCards(new FilterLandCard(), game);
if (lands.size() < 2 || lands.size() > hand.size() - 2)
return true;
return false;
}
@Override
public boolean chooseTarget(Outcome outcome, Target target, Game game) {
logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString());
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
if (logger.isLoggable(Level.FINE))
logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString());
UUID opponentId = game.getOpponents(playerId).iterator().next();
if (target instanceof TargetPlayer) {
if (outcome.isGood()) {
if (target.canTarget(playerId, game)) {
target.addTarget(playerId, game);
if (target.canTarget(playerId, source, game)) {
target.addTarget(playerId, source, game);
return true;
}
}
else {
if (target.canTarget(playerId, game)) {
target.addTarget(opponentId, game);
if (target.canTarget(playerId, source, game)) {
target.addTarget(opponentId, source, game);
return true;
}
}
@ -154,15 +164,15 @@ public class ComputerPlayer extends PlayerImpl implements Player {
findPlayables(game);
if (unplayable.size() > 0) {
for (int i = unplayable.size() - 1; i >= 0; i--) {
if (target.canTarget(unplayable.values().toArray(new Card[0])[i].getId(), game)) {
target.addTarget(unplayable.values().toArray(new Card[0])[i].getId(), game);
if (target.canTarget(unplayable.values().toArray(new Card[0])[i].getId(), source, game)) {
target.addTarget(unplayable.values().toArray(new Card[0])[i].getId(), source, game);
return true;
}
}
}
if (hand.size() > 0) {
if (target.canTarget(hand.keySet().toArray(new UUID[0])[0], game)) {
target.addTarget(hand.keySet().toArray(new UUID[0])[0], game);
if (target.canTarget(hand.toArray(new UUID[0])[0], source, game)) {
target.addTarget(hand.toArray(new UUID[0])[0], source, game);
return true;
}
}
@ -173,8 +183,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (!outcome.isGood())
Collections.reverse(targets);
for (Permanent permanent: targets) {
if (target.canTarget(permanent.getId(), game)) {
target.addTarget(permanent.getId(), game);
if (target.canTarget(permanent.getId(), source, game)) {
target.addTarget(permanent.getId(), source, game);
return true;
}
}
@ -188,8 +198,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
targets = threats(opponentId, ((TargetPermanent)target).getFilter(), game);
}
for (Permanent permanent: targets) {
if (target.canTarget(permanent.getId(), game)) {
target.addTarget(permanent.getId(), game);
if (target.canTarget(permanent.getId(), source, game)) {
target.addTarget(permanent.getId(), source, game);
return true;
}
}
@ -198,12 +208,13 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Game game) {
logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString());
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
if (logger.isLoggable(Level.FINE))
logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString());
UUID opponentId = game.getOpponents(playerId).iterator().next();
if (target instanceof TargetCreatureOrPlayerAmount) {
if (game.getPlayer(opponentId).getLife() <= target.getAmountRemaining()) {
target.addTarget(opponentId, target.getAmountRemaining(), game);
target.addTarget(opponentId, target.getAmountRemaining(), source, game);
return true;
}
List<Permanent> targets;
@ -214,9 +225,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
targets = threats(opponentId, new FilterCreaturePermanent(), game);
}
for (Permanent permanent: targets) {
if (target.canTarget(permanent.getId(), game)) {
if (target.canTarget(permanent.getId(), source, game)) {
if (permanent.getToughness().getValue() <= target.getAmountRemaining()) {
target.addTarget(permanent.getId(), permanent.getToughness().getValue(), game);
target.addTarget(permanent.getId(), permanent.getToughness().getValue(), source, game);
return true;
}
}
@ -304,17 +315,17 @@ public class ComputerPlayer extends PlayerImpl implements Player {
protected void playLand(Game game) {
logger.fine("playLand");
List<Card> lands = hand.getCards(new FilterLandCard());
Set<Card> lands = hand.getCards(new FilterLandCard(), game);
while (lands.size() > 0 && this.landsPlayed < this.landsPerTurn) {
if (lands.size() == 1)
this.playLand(lands.get(0), game);
this.playLand(lands.iterator().next(), game);
else {
playALand(lands, game);
}
}
}
protected void playALand(List<Card> lands, Game game) {
protected void playALand(Set<Card> lands, Game game) {
logger.fine("playALand");
//play a land that will allow us to play an unplayable
for (Mana mana: unplayable.keySet()) {
@ -341,8 +352,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
//play first available land
this.playLand(lands.get(0), game);
lands.remove(0);
this.playLand(lands.iterator().next(), game);
lands.remove(lands.iterator().next());
}
protected void findPlayables(Game game) {
@ -350,7 +361,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
playableNonInstant.clear();
unplayable.clear();
playableAbilities.clear();
List<Card> nonLands = hand.getCards(new FilterNonlandCard());
Set<Card> nonLands = hand.getCards(new FilterNonlandCard(), game);
ManaOptions available = getManaAvailable(game);
available.addMana(manaPool.getMana());
@ -402,7 +413,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
}
logger.fine("findPlayables: " + playableInstant.toString() + "---" + playableNonInstant.toString() + "---" + playableAbilities.toString() );
if (logger.isLoggable(Level.FINE))
logger.fine("findPlayables: " + playableInstant.toString() + "---" + playableNonInstant.toString() + "---" + playableAbilities.toString() );
}
@Override
@ -417,7 +429,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
ManaCost cost;
List<Permanent> producers;
if (unpaid instanceof ManaCosts) {
cost = ((ManaCosts)unpaid).get(0);
cost = ((ManaCosts<ManaCost>)unpaid).get(0);
producers = getSortedProducers((ManaCosts)unpaid, game);
}
else {
@ -477,7 +489,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
* @param game
* @return List<Permanent>
*/
private List<Permanent> getSortedProducers(ManaCosts unpaid, Game game) {
private List<Permanent> getSortedProducers(ManaCosts<ManaCost> unpaid, Game game) {
List<Permanent> unsorted = this.getAvailableManaProducers(game);
Map<Permanent, Integer> scored = new HashMap<Permanent, Integer>();
for (Permanent permanent: unsorted) {
@ -490,6 +502,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
}
if (score > 0) // score mana producers that produce other types higher
score += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size();
scored.put(permanent, score);
}
return sortByValue(scored);
@ -546,15 +560,19 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
@Override
public boolean chooseTarget(Cards cards, TargetCard target, Game game) {
public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) {
logger.fine("chooseTarget");
//TODO: improve this
//return first match
for (Card card: cards.getCards(target.getFilter())) {
target.addTarget(card.getId(), game);
return true;
if (!target.doneChosing()) {
for (Card card: cards.getCards(target.getFilter(), game)) {
target.addTarget(card.getId(), source, game);
if (target.doneChosing())
return true;
}
return false;
}
return false;
return true;
}
@Override
@ -665,12 +683,12 @@ public class ComputerPlayer extends PlayerImpl implements Player {
return potential;
}
protected List<Permanent> getAvailableBlockers(Game game) {
logger.fine("getAvailableBlockers");
FilterCreatureForCombat blockFilter = new FilterCreatureForCombat();
List<Permanent> blockers = game.getBattlefield().getAllActivePermanents(blockFilter, playerId);
return blockers;
}
// protected List<Permanent> getAvailableBlockers(Game game) {
// logger.fine("getAvailableBlockers");
// FilterCreatureForCombat blockFilter = new FilterCreatureForCombat();
// List<Permanent> blockers = game.getBattlefield().getAllActivePermanents(blockFilter, playerId);
// return blockers;
// }
protected List<Permanent> getOpponentBlockers(UUID opponentId, Game game) {
logger.fine("getOpponentBlockers");
@ -784,10 +802,24 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
protected void logState(Game game) {
if (logger.isLoggable(Level.FINE))
logList("computer player hand: ", new ArrayList(hand.getCards(game)));
}
protected void logList(String message, List<MageObject> list) {
StringBuilder sb = new StringBuilder();
sb.append("computer player hand: ");
for (Card card: hand.values()) {
sb.append(card.getName()).append(",");
sb.append(message).append(": ");
for (MageObject object: list) {
sb.append(object.getName()).append(",");
}
logger.fine(sb.toString());
}
protected void logAbilityList(String message, List<Ability> list) {
StringBuilder sb = new StringBuilder();
sb.append(message).append(": ");
for (Ability ability: list) {
sb.append(ability.getRule()).append(",");
}
logger.fine(sb.toString());
}
@ -798,7 +830,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (card.getSpellAbility().canActivate(playerId, game)) {
for (Effect effect: card.getSpellAbility().getEffects()) {
if (effect.getOutcome().equals(Outcome.DestroyPermanent)) {
if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, game)) {
if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, card.getSpellAbility(), game)) {
if (this.activateAbility(card.getSpellAbility(), game))
return;
}
@ -816,7 +848,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (card.getSpellAbility().canActivate(playerId, game)) {
for (Effect effect: card.getSpellAbility().getEffects()) {
if (effect instanceof DamageTargetEffect) {
if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, game)) {
if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, card.getSpellAbility(), game)) {
if (((DamageTargetEffect)effect).getAmount() > (creature.getPower().getValue() - creature.getDamage())) {
if (this.activateAbility(card.getSpellAbility(), game))
return;
@ -837,5 +869,10 @@ public class ComputerPlayer extends PlayerImpl implements Player {
playableAbilities = new ArrayList<ActivatedAbility>();
}
@Override
public T copy() {
return (T)new ComputerPlayer(this);
}
}

View file

@ -0,0 +1,7 @@
maxDepth=10
maxNodes=5000
evaluatorLifeFactor=2
evaluatorPermanentFactor=1
evaluatorCreatureFactor=1
evaluatorHandFactor=1
maxThinkSeconds=30

View file

@ -0,0 +1,75 @@
application.title=Mage.Player.AIMinimax
application.vendor=BetaSteward_at_googlemail.com
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/Mage.Player.AIMinimax.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
includes=**
jar.compress=false
javac.classpath=\
${reference.Mage.jar}:\
${reference.Mage_Player_AI.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.source=1.5
javac.target=1.5
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}:\
${libs.junit.classpath}:\
${libs.junit_4.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
jnlp.codebase.type=local
jnlp.descriptor=application
jnlp.enabled=false
jnlp.offline-allowed=false
jnlp.signed=false
meta.inf.dir=${src.dir}/META-INF
platform.active=default_platform
project.license=bsd
project.Mage=../Mage
project.Mage_Player_AI=../Mage.Player.AI
reference.Mage.jar=${project.Mage}/dist/Mage.jar
reference.Mage_Player_AI.jar=${project.Mage_Player_AI}/dist/Mage.Player.AI.jar
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
# or test-sys-prop.name=value to set system properties for unit tests):
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>Mage.Player.AIMinimax</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
<references xmlns="http://www.netbeans.org/ns/ant-project-references/1">
<reference>
<foreign-project>Mage</foreign-project>
<artifact-type>jar</artifact-type>
<script>build.xml</script>
<target>jar</target>
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
<reference>
<foreign-project>Mage_Player_AI</foreign-project>
<artifact-type>jar</artifact-type>
<script>build.xml</script>
<target>jar</target>
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
</references>
</configuration>
</project>

View file

@ -0,0 +1,52 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import mage.game.permanent.Permanent;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class Attackers extends TreeMap<Integer, List<Permanent>> {
public List<Permanent> getAttackers() {
List<Permanent> attackers = new ArrayList<Permanent>();
for (List<Permanent> l: this.values()) {
for (Permanent permanent: l) {
attackers.add(permanent);
}
}
return attackers;
}
}

View file

@ -0,0 +1,631 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.Constants.Outcome;
import mage.Constants.PhaseStep;
import mage.Constants.RangeOfInfluence;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.SearchEffect;
import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.filter.FilterAbility;
import mage.game.Game;
import mage.game.combat.Combat;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
import mage.game.turn.BeginCombatStep;
import mage.game.turn.BeginningPhase;
import mage.game.turn.CleanupStep;
import mage.game.turn.CombatDamageStep;
import mage.game.turn.CombatPhase;
import mage.game.turn.DeclareAttackersStep;
import mage.game.turn.DeclareBlockersStep;
import mage.game.turn.DrawStep;
import mage.game.turn.EndOfCombatStep;
import mage.game.turn.EndPhase;
import mage.game.turn.EndStep;
import mage.game.turn.Phase;
import mage.game.turn.PostCombatMainPhase;
import mage.game.turn.PostCombatMainStep;
import mage.game.turn.PreCombatMainPhase;
import mage.game.turn.PreCombatMainStep;
import mage.game.turn.UntapStep;
import mage.game.turn.UpkeepStep;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetCard;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements Player {
private static final transient Logger logger = Logging.getLogger(ComputerPlayer2.class.getName());
private static final ExecutorService pool = Executors.newFixedThreadPool(1);
protected int maxDepth;
protected int maxNodes;
protected LinkedList<Ability> actions = new LinkedList<Ability>();
protected List<UUID> targets = new ArrayList<UUID>();
protected List<String> choices = new ArrayList<String>();
protected Combat combat;
protected int currentScore;
protected SimulationNode root;
public ComputerPlayer2(String name, Deck deck, RangeOfInfluence range) {
super(name, deck, range);
maxDepth = Config.maxDepth;
maxNodes = Config.maxNodes;
}
public ComputerPlayer2(final ComputerPlayer2 player) {
super(player);
this.maxDepth = player.maxDepth;
this.currentScore = player.currentScore;
if (player.combat != null)
this.combat = player.combat.copy();
for (Ability ability: player.actions) {
actions.add(ability);
}
for (UUID targetId: player.targets) {
targets.add(targetId);
}
for (String choice: player.choices) {
choices.add(choice);
}
}
@Override
public ComputerPlayer2 copy() {
return new ComputerPlayer2(this);
}
@Override
public void priority(Game game) {
logState(game);
game.firePriorityEvent(playerId);
switch (game.getTurn().getStepType()) {
case UPKEEP:
case DRAW:
pass();
break;
case PRECOMBAT_MAIN:
case BEGIN_COMBAT:
case DECLARE_ATTACKERS:
case DECLARE_BLOCKERS:
case COMBAT_DAMAGE:
case END_COMBAT:
case POSTCOMBAT_MAIN:
if (actions.size() == 0) {
calculateActions(game);
}
act(game);
break;
case END_TURN:
case CLEANUP:
pass();
break;
}
}
protected void act(Game game) {
if (actions == null || actions.size() == 0)
pass();
else {
boolean usedStack = false;
while (actions.peek() != null) {
Ability ability = actions.poll();
this.activateAbility((ActivatedAbility) ability, game);
if (ability.isUsesStack())
usedStack = true;
}
if (usedStack)
pass();
}
}
protected void calculateActions(Game game) {
currentScore = GameStateEvaluator.evaluate(playerId, game);
if (!getNextAction(game)) {
Game sim = createSimulation(game);
SimulationNode.resetCount();
root = new SimulationNode(sim, maxDepth, playerId);
logger.fine("simulating actions");
addActionsTimed(new FilterAbility());
if (root.children.size() > 0) {
root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities);
combat = root.combat;
}
}
}
protected boolean getNextAction(Game game) {
if (root != null && root.children.size() > 0) {
SimulationNode test = root;
root = root.children.get(0);
while (root.children.size() > 0 && !root.playerId.equals(playerId)) {
test = root;
root = root.children.get(0);
}
logger.fine("simlating -- game value:" + game.getState().getValue() + " test value:" + test.gameValue);
if (root.playerId.equals(playerId) && root.abilities != null && game.getState().getValue() == test.gameValue) {
logger.fine("simulating -- continuing previous action chain");
actions = new LinkedList<Ability>(root.abilities);
combat = root.combat;
return true;
}
else {
return false;
}
}
return false;
}
protected int minimaxAB(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) {
UUID currentPlayerId = node.getGame().getPlayerList().get();
SimulationNode bestChild = null;
for (SimulationNode child: node.getChildren()) {
if (alpha >= beta) {
logger.fine("alpha beta pruning");
break;
}
if (SimulationNode.nodeCount > maxNodes) {
logger.fine("simulating -- reached end-state");
break;
}
int val = addActions(child, filter, depth-1, alpha, beta);
if (!currentPlayerId.equals(playerId)) {
if (val < beta) {
beta = val;
bestChild = child;
if (node.getCombat() == null)
node.setCombat(child.getCombat());
}
}
else {
if (val > alpha) {
alpha = val;
bestChild = child;
if (node.getCombat() == null)
node.setCombat(child.getCombat());
}
}
}
node.children.clear();
if (bestChild != null)
node.children.add(bestChild);
if (!currentPlayerId.equals(playerId)) {
logger.fine("returning minimax beta: " + beta);
return beta;
}
else {
logger.fine("returning minimax alpha: " + alpha);
return alpha;
}
}
protected SearchEffect getSearchEffect(StackAbility ability) {
for (Effect effect: ability.getEffects()) {
if (effect instanceof SearchEffect) {
return (SearchEffect) effect;
}
}
return null;
}
protected void resolve(SimulationNode node, int depth, Game game) {
StackObject ability = game.getStack().pop();
if (ability instanceof StackAbility) {
SearchEffect effect = getSearchEffect((StackAbility) ability);
if (effect != null && ability.getControllerId().equals(playerId)) {
Target target = effect.getTarget();
if (!target.doneChosing()) {
for (UUID targetId: target.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) {
Game sim = game.copy();
StackAbility newAbility = (StackAbility) ability.copy();
SearchEffect newEffect = getSearchEffect((StackAbility) newAbility);
newEffect.getTarget().addTarget(targetId, newAbility, sim);
sim.getStack().push(newAbility);
SimulationNode newNode = new SimulationNode(sim, depth, ability.getControllerId());
node.children.add(newNode);
newNode.getTargets().add(targetId);
logger.fine("simulating search -- node#: " + SimulationNode.getCount() + "for player: " + sim.getPlayer(ability.getControllerId()).getName());
}
return;
}
}
}
logger.fine("simulating resolve ");
ability.resolve(game);
game.applyEffects();
game.getPlayers().resetPassed();
game.getPlayerList().setCurrent(game.getActivePlayerId());
}
protected void addActionsTimed(final FilterAbility filter) {
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
public Integer call() throws Exception
{
return addActions(root, filter, maxDepth, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
});
pool.execute(task);
try {
task.get(Config.maxThinkSeconds, TimeUnit.SECONDS);
} catch (TimeoutException e) {
logger.fine("simulating - timed out");
task.cancel(true);
} catch (ExecutionException e) {
} catch (InterruptedException e) {
}
}
protected int addActions(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) {
Game game = node.getGame();
int val;
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
if (depth <= 0 || SimulationNode.nodeCount > maxNodes || game.isGameOver()) {
logger.fine("simulating -- reached end state");
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (node.getChildren().size() > 0) {
logger.fine("simulating -- somthing added children:" + node.getChildren().size());
val = minimaxAB(node, filter, depth-1, alpha, beta);
}
else {
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + (node.getPlayerId().equals(playerId)?"yes":"no"));
if (allPassed(game)) {
if (!game.getStack().isEmpty()) {
resolve(node, depth, game);
}
else {
// int testScore = GameStateEvaluator.evaluate(playerId, game);
// if (testScore < currentScore) {
// // if score at end of step is worse than original score don't check any further
// logger.fine("simulating -- abandoning current check, no immediate benefit");
// return testScore;
// }
game.getPlayers().resetPassed();
playNext(game, game.getActivePlayerId(), node);
}
}
if (game.isGameOver()) {
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (node.getChildren().size() > 0) {
//declared attackers or blockers or triggered abilities
logger.fine("simulating -- attack/block/trigger added children:" + node.getChildren().size());
val = minimaxAB(node, filter, depth-1, alpha, beta);
}
else {
val = simulatePriority(node, game, filter, depth, alpha, beta);
}
}
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- score: " + val + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(node.getPlayerId()).getName());
return val;
}
protected int simulatePriority(SimulationNode node, Game game, FilterAbility filter, int depth, int alpha, int beta) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
node.setGameValue(game.getState().getValue());
SimulatedPlayer currentPlayer = (SimulatedPlayer) game.getPlayer(game.getPlayerList().get());
SimulationNode bestNode = null;
List<Ability> allActions = currentPlayer.simulatePriority(game, filter);
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- adding " + allActions.size() + " children:" + allActions);
for (Ability action: allActions) {
Game sim = game.copy();
if (sim.getPlayer(playerId).activateAbility((ActivatedAbility) action.copy(), sim)) {
sim.applyEffects();
if (!sim.isGameOver() && action.isUsesStack()) {
// only pass if the last action uses the stack
sim.getPlayer(currentPlayer.getId()).pass();
sim.getPlayerList().getNext();
}
SimulationNode newNode = new SimulationNode(sim, action, depth, currentPlayer.getId());
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- node #:" + SimulationNode.getCount() + " actions:" + action);
sim.checkStateAndTriggered();
int val = addActions(newNode, filter, depth-1, alpha, beta);
if (!currentPlayer.getId().equals(playerId)) {
if (val < beta) {
beta = val;
bestNode = newNode;
node.setCombat(newNode.getCombat());
}
}
else {
if (val > alpha) {
alpha = val;
bestNode = newNode;
node.setCombat(newNode.getCombat());
if (node.getTargets().size() > 0)
targets = node.getTargets();
if (node.getChoices().size() > 0)
choices = node.getChoices();
}
}
if (alpha >= beta) {
logger.fine("simulating -- pruning");
break;
}
if (SimulationNode.nodeCount > maxNodes) {
logger.fine("simulating -- reached end-state");
break;
}
}
}
if (bestNode != null) {
node.children.clear();
node.children.add(bestNode);
}
if (!currentPlayer.getId().equals(playerId)) {
logger.fine("returning priority beta: " + beta);
return beta;
}
else {
logger.fine("returning priority alpha: " + alpha);
return alpha;
}
}
protected boolean allPassed(Game game) {
for (Player player: game.getPlayers().values()) {
if (!player.isPassed() && !player.hasLost() && !player.hasLeft())
return false;
}
return true;
}
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
if (choices.size() == 0)
return super.choose(outcome, choice, game);
if (!choice.isChosen()) {
for (String achoice: choices) {
choice.setChoice(achoice);
if (choice.isChosen()) {
choices.clear();
return true;
}
}
return false;
}
return true;
}
@Override
public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) {
if (targets.size() == 0)
return super.chooseTarget(cards, target, source, game);
if (!target.doneChosing()) {
for (UUID targetId: targets) {
target.addTarget(targetId, source, game);
if (target.doneChosing()) {
targets.clear();
return true;
}
}
return false;
}
return true;
}
public void playNext(Game game, UUID activePlayerId, SimulationNode node) {
boolean skip = false;
while (true) {
Phase currentPhase = game.getPhase();
if (!skip)
currentPhase.getStep().endStep(game, activePlayerId);
game.applyEffects();
switch (currentPhase.getStep().getType()) {
case UNTAP:
game.getPhase().setStep(new UpkeepStep());
break;
case UPKEEP:
game.getPhase().setStep(new DrawStep());
break;
case DRAW:
game.getTurn().setPhase(new PreCombatMainPhase());
game.getPhase().setStep(new PreCombatMainStep());
break;
case PRECOMBAT_MAIN:
game.getTurn().setPhase(new CombatPhase());
game.getPhase().setStep(new BeginCombatStep());
break;
case BEGIN_COMBAT:
game.getPhase().setStep(new DeclareAttackersStep());
break;
case DECLARE_ATTACKERS:
game.getPhase().setStep(new DeclareBlockersStep());
break;
case DECLARE_BLOCKERS:
game.getPhase().setStep(new CombatDamageStep(true));
break;
case COMBAT_DAMAGE:
if (((CombatDamageStep)currentPhase.getStep()).getFirst())
game.getPhase().setStep(new CombatDamageStep(false));
else
game.getPhase().setStep(new EndOfCombatStep());
break;
case END_COMBAT:
game.getTurn().setPhase(new PostCombatMainPhase());
game.getPhase().setStep(new PostCombatMainStep());
break;
case POSTCOMBAT_MAIN:
game.getTurn().setPhase(new EndPhase());
game.getPhase().setStep(new EndStep());
break;
case END_TURN:
game.getPhase().setStep(new CleanupStep());
break;
case CLEANUP:
game.getPhase().getStep().beginStep(game, activePlayerId);
if (!game.checkStateAndTriggered() && !game.isGameOver()) {
game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext());
game.getTurn().setPhase(new BeginningPhase());
game.getPhase().setStep(new UntapStep());
}
}
if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, activePlayerId));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) {
for (Combat engagement: ((SimulatedPlayer)game.getPlayer(activePlayerId)).addAttackers(game)) {
Game sim = game.copy();
UUID defenderId = game.getOpponents(playerId).iterator().next();
for (CombatGroup group: engagement.getGroups()) {
for (UUID attackerId: group.getAttackers()) {
sim.getPlayer(activePlayerId).declareAttacker(attackerId, defenderId, sim);
}
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, playerId, playerId));
SimulationNode newNode = new SimulationNode(sim, node.getDepth()-1, activePlayerId);
logger.fine("simulating -- node #:" + SimulationNode.getCount() + " declare attakers");
newNode.setCombat(sim.getCombat());
node.children.add(newNode);
}
}
}
else if (game.getTurn().getStepType() == PhaseStep.DECLARE_BLOCKERS) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, activePlayerId));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, activePlayerId, activePlayerId))) {
for (UUID defenderId: game.getCombat().getDefenders()) {
//check if defender is being attacked
if (game.getCombat().isAttacked(defenderId, game)) {
for (Combat engagement: ((SimulatedPlayer)game.getPlayer(defenderId)).addBlockers(game)) {
Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) {
for (UUID blockerId: group.getBlockers()) {
group.addBlocker(blockerId, defenderId, sim);
}
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId));
SimulationNode newNode = new SimulationNode(sim, node.getDepth()-1, defenderId);
logger.fine("simulating -- node #:" + SimulationNode.getCount() + " declare blockers");
newNode.setCombat(sim.getCombat());
node.children.add(newNode);
}
}
}
}
}
else {
game.getStep().beginStep(game, activePlayerId);
}
if (game.getStep().getHasPriority())
break;
}
else {
skip = true;
}
}
game.checkStateAndTriggered();
}
@Override
public void selectAttackers(Game game) {
logger.fine("selectAttackers");
if (combat != null) {
UUID opponentId = game.getCombat().getDefenders().iterator().next();
for (UUID attackerId: combat.getAttackers()) {
this.declareAttacker(attackerId, opponentId, game);
}
}
}
@Override
public void selectBlockers(Game game) {
logger.fine("selectBlockers");
if (combat != null && combat.getGroups().size() > 0) {
List<CombatGroup> groups = game.getCombat().getGroups();
for (int i = 0; i< groups.size(); i++) {
for (UUID blockerId: combat.getGroups().get(i).getBlockers()) {
this.declareBlocker(blockerId, groups.get(i).getAttackers().get(0), game);
}
}
}
}
/**
* Copies game and replaces all players in copy with simulated players
*
* @param game
* @return a new game object with simulated players
*/
protected Game createSimulation(Game game) {
Game sim = game.copy();
for (Player copyPlayer: sim.getState().getPlayers().values()) {
Player origPlayer = game.getState().getPlayers().get(copyPlayer.getId());
SimulatedPlayer newPlayer = new SimulatedPlayer(copyPlayer.getId(), copyPlayer.getId().equals(playerId));
newPlayer.restore(origPlayer);
sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer);
}
return sim;
}
}

View file

@ -0,0 +1,540 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.LinkedList;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.Constants.AbilityType;
import mage.Constants.PhaseStep;
import mage.Constants.RangeOfInfluence;
import mage.Constants.Zone;
import mage.abilities.Ability;
import mage.cards.decks.Deck;
import mage.filter.FilterAbility;
import mage.game.Game;
import mage.game.combat.Combat;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import mage.game.turn.BeginCombatStep;
import mage.game.turn.BeginningPhase;
import mage.game.turn.CleanupStep;
import mage.game.turn.CombatDamageStep;
import mage.game.turn.CombatPhase;
import mage.game.turn.DeclareAttackersStep;
import mage.game.turn.DeclareBlockersStep;
import mage.game.turn.DrawStep;
import mage.game.turn.EndOfCombatStep;
import mage.game.turn.EndPhase;
import mage.game.turn.EndStep;
import mage.game.turn.PostCombatMainPhase;
import mage.game.turn.PostCombatMainStep;
import mage.game.turn.Step;
import mage.game.turn.UntapStep;
import mage.game.turn.UpkeepStep;
import mage.players.Player;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
private static final transient Logger logger = Logging.getLogger(ComputerPlayer3.class.getName());
private static FilterAbility filterLand = new FilterAbility();
private static FilterAbility filterNotLand = new FilterAbility();
static {
filterLand.getTypes().add(AbilityType.PLAY_LAND);
filterLand.setZone(Zone.HAND);
filterNotLand.getTypes().add(AbilityType.PLAY_LAND);
filterNotLand.setZone(Zone.HAND);
filterNotLand.setNotFilter(true);
}
public ComputerPlayer3(String name, Deck deck, RangeOfInfluence range) {
super(name, deck, range);
maxDepth = Config.maxDepth;
maxNodes = Config.maxNodes;
}
public ComputerPlayer3(final ComputerPlayer3 player) {
super(player);
}
@Override
public ComputerPlayer3 copy() {
return new ComputerPlayer3(this);
}
@Override
public void priority(Game game) {
logState(game);
game.firePriorityEvent(playerId);
switch (game.getTurn().getStepType()) {
case UPKEEP:
case DRAW:
pass();
break;
case PRECOMBAT_MAIN:
if (game.getActivePlayerId().equals(playerId)) {
if (actions.size() == 0) {
calculatePreCombatActions(game);
}
act(game);
}
else
pass();
break;
case BEGIN_COMBAT:
pass();
break;
case DECLARE_ATTACKERS:
if (!game.getActivePlayerId().equals(playerId)) {
if (actions.size() == 0) {
calculatePreCombatActions(game);
}
act(game);
}
else
pass();
break;
case DECLARE_BLOCKERS:
case COMBAT_DAMAGE:
case END_COMBAT:
pass();
break;
case POSTCOMBAT_MAIN:
if (game.getActivePlayerId().equals(playerId)) {
if (actions.size() == 0) {
calculatePostCombatActions(game);
}
act(game);
}
else
pass();
break;
case END_TURN:
case CLEANUP:
pass();
break;
}
}
protected void calculatePreCombatActions(Game game) {
if (!getNextAction(game)) {
currentScore = GameStateEvaluator.evaluate(playerId, game);
Game sim = createSimulation(game);
SimulationNode.resetCount();
root = new SimulationNode(sim, maxDepth, playerId);
logger.fine("simulating pre combat actions -----------------------------------------------------------------------------------------");
addActionsTimed(new FilterAbility());
if (root.children.size() > 0) {
root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities);
combat = root.combat;
}
}
}
protected void calculatePostCombatActions(Game game) {
if (!getNextAction(game)) {
currentScore = GameStateEvaluator.evaluate(playerId, game);
Game sim = createSimulation(game);
SimulationNode.resetCount();
root = new SimulationNode(sim, maxDepth, playerId);
logger.fine("simulating post combat actions ----------------------------------------------------------------------------------------");
addActionsTimed(new FilterAbility());
if (root.children.size() > 0) {
root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities);
combat = root.combat;
}
}
}
@Override
protected int addActions(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) {
boolean stepFinished = false;
int val;
Game game = node.getGame();
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
if (depth <= 0 || SimulationNode.nodeCount > maxNodes || game.isGameOver()) {
logger.fine("simulating -- reached end state");
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (node.getChildren().size() > 0) {
logger.fine("simulating -- somthing added children:" + node.getChildren().size());
val = minimaxAB(node, filter, depth-1, alpha, beta);
}
else {
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName());
if (allPassed(game)) {
if (!game.getStack().isEmpty()) {
resolve(node, depth, game);
}
else {
stepFinished = true;
}
}
if (game.isGameOver()) {
val = GameStateEvaluator.evaluate(playerId, game);
}
else if (stepFinished) {
logger.fine("step finished");
int testScore = GameStateEvaluator.evaluate(playerId, game);
if (game.getActivePlayerId().equals(playerId)) {
if (testScore < currentScore) {
// if score at end of step is worse than original score don't check further
logger.fine("simulating -- abandoning check, no immediate benefit");
val = testScore;
}
else {
switch (game.getTurn().getStepType()) {
case PRECOMBAT_MAIN:
val = simulateCombat(game, node, depth-1, alpha, beta, false);
break;
case POSTCOMBAT_MAIN:
val = simulateCounterAttack(game, node, depth-1, alpha, beta);
break;
default:
val = GameStateEvaluator.evaluate(playerId, game);
break;
}
}
}
else {
if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS)
val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true);
else
val = GameStateEvaluator.evaluate(playerId, game);
}
}
else if (node.getChildren().size() > 0) {
logger.fine("simulating -- trigger added children:" + node.getChildren().size());
val = minimaxAB(node, filter, depth, alpha, beta);
}
else {
val = simulatePriority(node, game, filter, depth, alpha, beta);
}
}
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- score: " + val + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(node.getPlayerId()).getName());
return val;
}
protected int simulateCombat(Game game, SimulationNode node, int depth, int alpha, int beta, boolean counter) {
Integer val = null;
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
if (game.getTurn().getStepType() != PhaseStep.DECLARE_BLOCKERS) {
game.getTurn().setPhase(new CombatPhase());
if (game.getPhase().beginPhase(game, game.getActivePlayerId())) {
simulateStep(game, new BeginCombatStep());
game.getPhase().setStep(new DeclareAttackersStep());
if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, game.getActivePlayerId()));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter);
}
}
else if (!counter) {
simulateToEnd(game);
val = simulatePostCombatMain(game, node, depth, alpha, beta);
}
}
}
else {
if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId()));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
//only suitable for two player games - only simulates blocks for 1st defender
val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter);
}
}
else if (!counter) {
finishCombat(game);
val = GameStateEvaluator.evaluate(playerId, game);
// val = simulateCounterAttack(game, node, depth, alpha, beta);
}
}
if (val == null)
val = GameStateEvaluator.evaluate(playerId, game);
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- combat score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName());
return val;
}
protected int simulateAttackers(Game game, SimulationNode node, UUID attackerId, int depth, int alpha, int beta, boolean counter) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
Integer val = null;
SimulationNode bestNode = null;
SimulatedPlayer attacker = (SimulatedPlayer) game.getPlayer(attackerId);
for (Combat engagement: attacker.addAttackers(game)) {
if (alpha >= beta) {
logger.fine("simulating -- pruning attackers");
break;
}
Game sim = game.copy();
UUID defenderId = game.getOpponents(playerId).iterator().next();
for (CombatGroup group: engagement.getGroups()) {
for (UUID attackId: group.getAttackers()) {
sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim);
}
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, playerId, playerId));
SimulationNode newNode = new SimulationNode(sim, depth, game.getActivePlayerId());
if (logger.isLoggable(Level.FINE))
logger.fine("simulating attack -- node#: " + SimulationNode.getCount());
sim.checkStateAndTriggered();
while (!sim.getStack().isEmpty()) {
sim.getStack().resolve(sim);
logger.fine("resolving triggered abilities");
sim.applyEffects();
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy();
sim.getPhase().setStep(new DeclareBlockersStep());
val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter);
if (!attackerId.equals(playerId)) {
if (val < beta) {
beta = val;
bestNode = newNode;
node.setCombat(simCombat);
}
}
else {
if (val > alpha) {
alpha = val;
bestNode = newNode;
node.setCombat(simCombat);
}
}
}
if (val == null)
val = GameStateEvaluator.evaluate(playerId, game);
if (bestNode != null) {
node.children.clear();
node.children.add(bestNode);
}
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- combat attacker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName());
return val;
}
protected int simulateBlockers(Game game, SimulationNode node, UUID defenderId, int depth, int alpha, int beta, boolean counter) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
Integer val = null;
SimulationNode bestNode = null;
//check if defender is being attacked
if (game.getCombat().isAttacked(defenderId, game)) {
SimulatedPlayer defender = (SimulatedPlayer) game.getPlayer(defenderId);
for (Combat engagement: defender.addBlockers(game)) {
if (alpha >= beta) {
logger.fine("simulating -- pruning blockers");
break;
}
Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) {
UUID attackerId = group.getAttackers().get(0);
for (UUID blockerId: group.getBlockers()) {
sim.getPlayer(defenderId).declareBlocker(blockerId, attackerId, sim);
}
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId));
SimulationNode newNode = new SimulationNode(sim, depth, defenderId);
if (logger.isLoggable(Level.FINE))
logger.fine("simulating block -- node#: " + SimulationNode.getCount());
sim.checkStateAndTriggered();
while (!sim.getStack().isEmpty()) {
sim.getStack().resolve(sim);
logger.fine("resolving triggered abilities");
sim.applyEffects();
}
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy();
finishCombat(sim);
if (!counter) {
int testScore = GameStateEvaluator.evaluate(playerId, sim);
if (testScore < currentScore) {
// if score at end of combat is worse than original score don't check counterattack
logger.fine("simulating -- abandoning counterattack check, no immediate benefit");
val = testScore;
}
else
val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta);
}
else
val = GameStateEvaluator.evaluate(playerId, sim);
if (!defenderId.equals(playerId)) {
if (val < beta) {
beta = val;
bestNode = newNode;
node.setCombat(simCombat);
}
}
else {
if (val > alpha) {
alpha = val;
bestNode = newNode;
node.setCombat(simCombat);
}
}
}
}
if (val == null)
val = GameStateEvaluator.evaluate(playerId, game);
if (bestNode != null) {
node.children.clear();
node.children.add(bestNode);
}
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- combat blocker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName());
return val;
}
protected int simulateCounterAttack(Game game, SimulationNode node, int depth, int alpha, int beta) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
Integer val = null;
if (!game.isGameOver()) {
logger.fine("simulating -- counter attack");
simulateToEnd(game);
game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext());
game.getTurn().setPhase(new BeginningPhase());
if (game.getPhase().beginPhase(game, game.getActivePlayerId())) {
simulateStep(game, new UntapStep());
simulateStep(game, new UpkeepStep());
simulateStep(game, new DrawStep());
game.getPhase().endPhase(game, game.getActivePlayerId());
}
val = simulateCombat(game, node, depth-1, alpha, beta, true);
if (logger.isLoggable(Level.FINE))
logger.fine("returning -- counter attack score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName());
}
if (val == null)
val = GameStateEvaluator.evaluate(playerId, game);
return val;
}
protected void simulateStep(Game game, Step step) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return;
}
if (!game.isGameOver()) {
game.getPhase().setStep(step);
if (!step.skipStep(game, game.getActivePlayerId())) {
step.beginStep(game, game.getActivePlayerId());
game.checkStateAndTriggered();
while (!game.getStack().isEmpty()) {
game.getStack().resolve(game);
game.applyEffects();
}
step.endStep(game, game.getActivePlayerId());
}
}
}
protected void finishCombat(Game game) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return;
}
simulateStep(game, new CombatDamageStep(true));
simulateStep(game, new CombatDamageStep(false));
simulateStep(game, new EndOfCombatStep());
}
protected int simulatePostCombatMain(Game game, SimulationNode node, int depth, int alpha, int beta) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return GameStateEvaluator.evaluate(playerId, game);
}
logger.fine("simulating -- post combat main");
game.getTurn().setPhase(new PostCombatMainPhase());
if (game.getPhase().beginPhase(game, game.getActivePlayerId())) {
game.getPhase().setStep(new PostCombatMainStep());
game.getStep().beginStep(game, playerId);
game.getPlayers().resetPassed();
return addActions(node, new FilterAbility(), depth, alpha, beta);
}
return simulateCounterAttack(game, node, depth, alpha, beta);
}
protected void simulateToEnd(Game game) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
logger.fine("interrupted");
return;
}
if (!game.isGameOver()) {
game.getTurn().getPhase().endPhase(game, game.getActivePlayerId());
game.getTurn().setPhase(new EndPhase());
if (game.getTurn().getPhase().beginPhase(game, game.getActivePlayerId())) {
simulateStep(game, new EndStep());
simulateStep(game, new CleanupStep());
}
}
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class Config {
private final static Logger logger = Logging.getLogger(Config.class.getName());
public static final int maxDepth;
public static final int maxNodes;
public static final int evaluatorLifeFactor;
public static final int evaluatorPermanentFactor;
public static final int evaluatorCreatureFactor;
public static final int evaluatorHandFactor;
public static final int maxThinkSeconds;
static {
Properties p = new Properties();
try {
File file = new File(Config.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
p.load(new FileInputStream(new File(file.getParent() + File.separator + "AIMinimax.properties")));
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
} catch (URISyntaxException ex) {
Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, ex);
}
maxDepth = Integer.parseInt(p.getProperty("maxDepth"));
maxNodes = Integer.parseInt(p.getProperty("maxNodes"));
evaluatorLifeFactor = Integer.parseInt(p.getProperty("evaluatorLifeFactor"));
evaluatorPermanentFactor = Integer.parseInt(p.getProperty("evaluatorPermanentFactor"));
evaluatorCreatureFactor = Integer.parseInt(p.getProperty("evaluatorCreatureFactor"));
evaluatorHandFactor = Integer.parseInt(p.getProperty("evaluatorHandFactor"));
maxThinkSeconds = Integer.parseInt(p.getProperty("maxThinkSeconds"));
}
}

View file

@ -0,0 +1,102 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package mage.player.ai;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.Constants.CardType;
import mage.Constants.Zone;
import mage.abilities.ActivatedAbility;
import mage.abilities.keyword.DoubleStrikeAbility;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.mana.ManaAbility;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*
* this evaluator is only good for two player games
*
*/
public class GameStateEvaluator {
private final static transient Logger logger = Logging.getLogger(GameStateEvaluator.class.getName());
private static final int LIFE_FACTOR = Config.evaluatorLifeFactor;
private static final int PERMANENT_FACTOR = Config.evaluatorPermanentFactor;
private static final int CREATURE_FACTOR = Config.evaluatorCreatureFactor;
private static final int HAND_FACTOR = Config.evaluatorHandFactor;
public static int evaluate(UUID playerId, Game game) {
Player player = game.getPlayer(playerId);
Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next());
if (game.isGameOver()) {
if (player.hasLost() || opponent.hasWon())
return Integer.MIN_VALUE;
if (opponent.hasLost() || player.hasWon())
return Integer.MAX_VALUE;
}
int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR;
int permanentScore = 0;
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) {
permanentScore += evaluatePermanent(permanent, game);
}
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(opponent.getId())) {
permanentScore -= evaluatePermanent(permanent, game);
}
permanentScore *= PERMANENT_FACTOR;
int handScore = 0;
handScore = 7 - opponent.getHand().size();
handScore = Math.min(7, player.getHand().size());
handScore *= HAND_FACTOR;
int score = lifeScore + permanentScore + handScore;
if (logger.isLoggable(Level.FINE))
logger.fine("game state evaluated to- lifeScore:" + lifeScore + " permanentScore:" + permanentScore + " handScore:" + handScore + " total:" + score);
return score;
}
public static int evaluatePermanent(Permanent permanent, Game game) {
int value = permanent.isTapped()?1:2;
if (permanent.getCardType().contains(CardType.CREATURE)) {
value += evaluateCreature(permanent, game) * CREATURE_FACTOR;
}
value += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size();
for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) {
if (!(ability instanceof ManaAbility) && ability.canActivate(ability.getControllerId(), game))
value += ability.getEffects().getOutcomeTotal();
}
value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).getOutcomeTotal();
value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).getOutcomeTotal();
value += permanent.getManaCost().convertedManaCost();
//TODO: add a difficulty to calculation to ManaCost - sort permanents by difficulty for casting when evaluating game states
return value;
}
public static int evaluateCreature(Permanent creature, Game game) {
int value = 0;
value += creature.getPower().getValue();
value += creature.getToughness().getValue();
// if (creature.canAttack(game))
// value += creature.getPower().getValue();
// if (!creature.isTapped())
// value += 2;
value += creature.getAbilities().getEvasionAbilities().getOutcomeTotal();
value += creature.getAbilities().getProtectionAbilities().getOutcomeTotal();
value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0;
value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0;
value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0;
return value;
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.game.Game;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class SimulateBlockWorker implements Callable {
private final static Logger logger = Logging.getLogger(SimulationWorker.class.getName());
private SimulationNode node;
private ComputerPlayer3 player;
public SimulateBlockWorker(ComputerPlayer3 player, SimulationNode node) {
this.player = player;
this.node = node;
}
@Override
public Object call() {
try {
// player.simulateBlock(node);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
return null;
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.List;
import mage.abilities.Ability;
import mage.game.Game;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class SimulatedAction {
private Game game;
private List<Ability> abilities;
public SimulatedAction(Game game, List<Ability> abilities) {
this.game = game;
this.abilities = abilities;
}
public Game getGame() {
return this.game;
}
public List<Ability> getAbilities() {
return this.abilities;
}
@Override
public String toString() {
return this.abilities.toString();
}
public boolean usesStack() {
if (abilities != null && abilities.size() > 0) {
return abilities.get(abilities.size() -1).isUsesStack();
}
return true;
}
}

View file

@ -0,0 +1,250 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.PassAbility;
import mage.abilities.mana.ManaOptions;
import mage.choices.Choice;
import mage.filter.FilterAbility;
import mage.game.Game;
import mage.game.combat.Combat;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackAbility;
import mage.target.Target;
import mage.util.Copier;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class SimulatedPlayer extends ComputerPlayer<SimulatedPlayer> {
private final static transient Logger logger = Logging.getLogger(SimulatedPlayer.class.getName());
private boolean isSimulatedPlayer;
private FilterAbility filter;
private transient ConcurrentLinkedQueue<Ability> allActions;
private static PassAbility pass = new PassAbility();
public SimulatedPlayer(UUID id, boolean isSimulatedPlayer) {
super(id);
pass.setControllerId(playerId);
this.isSimulatedPlayer = isSimulatedPlayer;
}
public SimulatedPlayer(final SimulatedPlayer player) {
super(player);
this.isSimulatedPlayer = player.isSimulatedPlayer;
if (player.filter != null)
this.filter = player.filter.copy();
}
@Override
public SimulatedPlayer copy() {
return new SimulatedPlayer(this);
}
public List<Ability> simulatePriority(Game game, FilterAbility filter) {
allActions = new ConcurrentLinkedQueue<Ability>();
Game sim = game.copy();
this.filter = filter;
simulateOptions(sim, pass);
ArrayList<Ability> list = new ArrayList<Ability>(allActions);
Collections.reverse(list);
return list;
}
protected void simulateOptions(Game game, Ability previousActions) {
allActions.add(previousActions);
ManaOptions available = getManaAvailable(game);
available.addMana(manaPool.getMana());
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, filter, available, isSimulatedPlayer);
for (Ability ability: playables) {
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
if (options.size() == 0) {
allActions.add(ability);
// simulateAction(game, previousActions, ability);
}
else {
// ExecutorService simulationExecutor = Executors.newFixedThreadPool(4);
for (Ability option: options) {
allActions.add(option);
// SimulationWorker worker = new SimulationWorker(game, this, previousActions, option);
// simulationExecutor.submit(worker);
}
// simulationExecutor.shutdown();
// while(!simulationExecutor.isTerminated()) {}
}
}
}
// protected void simulateAction(Game game, SimulatedAction previousActions, Ability action) {
// List<Ability> actions = new ArrayList<Ability>(previousActions.getAbilities());
// actions.add(action);
// Game sim = game.copy();
// if (sim.getPlayer(playerId).activateAbility((ActivatedAbility) action.copy(), sim)) {
// sim.applyEffects();
// sim.getPlayers().resetPassed();
// allActions.add(new SimulatedAction(sim, actions));
// }
// }
public List<Combat> addAttackers(Game game) {
Map<Integer, Combat> engagements = new HashMap<Integer, Combat>();
//useful only for two player games - will only attack first opponent
UUID defenderId = game.getOpponents(playerId).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(game);
//use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size());
StringBuilder binary = new StringBuilder();
for (int i = powerElements - 1; i >= 0; i--) {
Game sim = game.copy();
binary.setLength(0);
binary.append(Integer.toBinaryString(i));
while (binary.length() < attackersList.size()) {
binary.insert(0, "0");
}
for (int j = 0; j < attackersList.size(); j++) {
if (binary.charAt(j) == '1')
sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, sim);
}
if (engagements.put(sim.getCombat().getValue(sim), sim.getCombat()) != null) {
logger.fine("simulating -- found redundant attack combination");
}
else if (logger.isLoggable(Level.FINE)) {
logger.fine("simulating -- attack:" + sim.getCombat().getGroups().size());
}
}
return new ArrayList<Combat>(engagements.values());
}
public List<Combat> addBlockers(Game game) {
Map<Integer, Combat> engagements = new HashMap<Integer, Combat>();
int numGroups = game.getCombat().getGroups().size();
if (numGroups == 0) return new ArrayList<Combat>();
//add a node with no blockers
Game sim = game.copy();
engagements.put(sim.getCombat().getValue(sim), sim.getCombat());
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId));
List<Permanent> blockers = getAvailableBlockers(game);
addBlocker(game, blockers, engagements);
return new ArrayList<Combat>(engagements.values());
}
protected void addBlocker(Game game, List<Permanent> blockers, Map<Integer, Combat> engagements) {
if (blockers.size() == 0)
return;
int numGroups = game.getCombat().getGroups().size();
//try to block each attacker with each potential blocker
Permanent blocker = blockers.get(0);
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- block:" + blocker);
List<Permanent> remaining = remove(blockers, blocker);
for (int i = 0; i < numGroups; i++) {
if (game.getCombat().getGroups().get(i).canBlock(blocker, game)) {
Game sim = game.copy();
sim.getCombat().getGroups().get(i).addBlocker(blocker.getId(), playerId, sim);
if (engagements.put(sim.getCombat().getValue(sim), sim.getCombat()) != null)
logger.fine("simulating -- found redundant block combination");
addBlocker(sim, remaining, engagements); // and recurse minus the used blocker
}
}
addBlocker(game, remaining, engagements);
}
@Override
public boolean triggerAbility(TriggeredAbility source, Game game) {
Ability ability = source.copy();
List<Ability> options = getPlayableOptions(ability, game);
if (options.size() == 0) {
if (logger.isLoggable(Level.FINE))
logger.fine("simulating -- triggered ability:" + ability);
game.getStack().push(new StackAbility(ability, playerId));
ability.activate(game, false);
game.applyEffects();
game.getPlayers().resetPassed();
}
else {
SimulationNode parent = (SimulationNode) game.getCustomData();
int depth = parent.getDepth() - 1;
if (depth == 0) return true;
logger.fine("simulating -- triggered ability - adding children:" + options.size());
for (Ability option: options) {
addAbilityNode(parent, option, depth, game);
}
}
return true;
}
protected void addAbilityNode(SimulationNode parent, Ability ability, int depth, Game game) {
Game sim = game.copy();
sim.getStack().push(new StackAbility(ability, playerId));
ability.activate(sim, false);
sim.applyEffects();
SimulationNode newNode = new SimulationNode(sim, depth, playerId);
logger.fine("simulating -- node #:" + SimulationNode.getCount() + " triggered ability option");
for (Target target: ability.getTargets()) {
for (UUID targetId: target.getTargets()) {
newNode.getTargets().add(targetId);
}
}
for (Choice choice: ability.getChoices()) {
newNode.getChoices().add(choice.getChoice());
}
parent.children.add(newNode);
}
@Override
public void priority(Game game) {
//should never get here
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.game.Game;
import mage.game.combat.Combat;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class SimulationNode implements Serializable {
protected static int nodeCount;
protected Game game;
protected int gameValue;
protected List<Ability> abilities;
protected int depth;
protected List<SimulationNode> children = new ArrayList<SimulationNode>();
protected List<UUID> targets = new ArrayList<UUID>();
protected List<String> choices = new ArrayList<String>();
protected UUID playerId;
protected Combat combat;
public SimulationNode(Game game, int depth, UUID playerId) {
this.game = game;
this.depth = depth;
this.playerId = playerId;
game.setCustomData(this);
nodeCount++;
}
public SimulationNode(Game game, List<Ability> abilities, int depth, UUID playerId) {
this(game, depth, playerId);
this.abilities = abilities;
}
public SimulationNode(Game game, Ability ability, int depth, UUID playerId) {
this(game, depth, playerId);
this.abilities = new ArrayList<Ability>();
abilities.add(ability);
}
public static void resetCount() {
nodeCount = 0;
}
public static int getCount() {
return nodeCount;
}
public Game getGame() {
return this.game;
}
public int getGameValue() {
return this.gameValue;
}
public void setGameValue(int value) {
this.gameValue = value;
}
public List<Ability> getAbilities() {
return this.abilities;
}
public List<SimulationNode> getChildren() {
return this.children;
}
public int getDepth() {
return this.depth;
}
public UUID getPlayerId() {
return this.playerId;
}
public Combat getCombat() {
return this.combat;
}
public void setCombat(Combat combat) {
this.combat = combat;
}
public List<UUID> getTargets() {
return this.targets;
}
public List<String> getChoices() {
return this.choices;
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import mage.abilities.Ability;
import mage.game.Game;
import mage.util.Logging;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class SimulationWorker implements Callable {
private final static Logger logger = Logging.getLogger(SimulationWorker.class.getName());
private Game game;
private SimulatedAction previousActions;
private Ability action;
private SimulatedPlayer player;
public SimulationWorker(Game game, SimulatedPlayer player, SimulatedAction previousActions, Ability action) {
this.game = game;
this.player = player;
this.previousActions = previousActions;
this.action = action;
}
@Override
public Object call() {
try {
// player.simulateAction(game, previousActions, action);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
return null;
}
}

View file

@ -43,6 +43,7 @@ import mage.Constants.RangeOfInfluence;
import mage.Constants.TargetController;
import mage.Constants.Zone;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpecialAction;
import mage.abilities.costs.mana.ManaCost;
@ -63,11 +64,11 @@ import mage.target.common.TargetDefender;
*
* @author BetaSteward_at_googlemail.com
*/
public class HumanPlayer extends PlayerImpl {
public class HumanPlayer extends PlayerImpl<HumanPlayer> {
final transient PlayerResponse response = new PlayerResponse();
private boolean abort = false;
private boolean abort;
protected transient TargetPermanent targetCombat = new TargetPermanent(new FilterCreatureForCombat(), TargetController.YOU);
@ -76,6 +77,11 @@ public class HumanPlayer extends PlayerImpl {
human = true;
}
public HumanPlayer(final HumanPlayer player) {
super(player);
this.abort = player.abort;
}
protected void waitForResponse() {
response.clear();
synchronized(response) {
@ -135,23 +141,27 @@ public class HumanPlayer extends PlayerImpl {
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
game.fireChooseEvent(playerId, choice);
while (!abort) {
waitForStringResponse();
choice.setChoice(response.getString());
return true;
game.fireChooseEvent(playerId, choice);
waitForResponse();
if (response.getString() != null) {
choice.setChoice(response.getString());
return true;
} else if (!choice.isRequired()) {
return false;
}
}
return false;
}
@Override
public boolean chooseTarget(Outcome outcome, Target target, Game game) {
game.fireSelectTargetEvent(playerId, target.getMessage(), target.isRequired());
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
while (!abort) {
game.fireSelectTargetEvent(playerId, target.getMessage(), target.isRequired());
waitForResponse();
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), game)) {
target.addTarget(response.getUUID(), game);
if (target.canTarget(response.getUUID(), source, game)) {
target.addTarget(response.getUUID(), source, game);
return true;
}
} else if (!target.isRequired()) {
@ -162,13 +172,13 @@ public class HumanPlayer extends PlayerImpl {
}
@Override
public boolean chooseTarget(Cards cards, TargetCard target, Game game) {
public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) {
game.fireSelectTargetEvent(playerId, target.getMessage(), cards, target.isRequired());
while (!abort) {
waitForResponse();
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), cards, game)) {
target.addTarget(response.getUUID(), game);
target.addTarget(response.getUUID(), source, game);
return true;
}
} else if (!target.isRequired()) {
@ -180,15 +190,15 @@ public class HumanPlayer extends PlayerImpl {
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Game game) {
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
game.fireSelectTargetEvent(playerId, target.getMessage() + "\n Amount remaining:" + target.getAmountRemaining(), target.isRequired());
while (!abort) {
waitForResponse();
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), game)) {
if (target.canTarget(response.getUUID(), source, game)) {
UUID targetId = response.getUUID();
int amountSelected = getAmount(1, target.getAmountRemaining(), "Select amount", game);
target.addTarget(targetId, amountSelected, game);
target.addTarget(targetId, amountSelected, source, game);
return true;
}
} else if (!target.isRequired()) {
@ -313,7 +323,7 @@ public class HumanPlayer extends PlayerImpl {
if (response.getBoolean() != null) {
return;
} else if (response.getUUID() != null) {
if (targetCombat.canTarget(playerId, response.getUUID(), game)) {
if (targetCombat.canTarget(playerId, response.getUUID(), null, game)) {
selectDefender(game.getCombat().getDefenders(), response.getUUID(), game);
}
}
@ -321,11 +331,17 @@ public class HumanPlayer extends PlayerImpl {
}
protected boolean selectDefender(Set<UUID> defenders, UUID attackerId, Game game) {
TargetDefender target = new TargetDefender(defenders, attackerId);
if (chooseTarget(Outcome.Damage, target, game)) {
declareAttacker(attackerId, response.getUUID(), game);
if (defenders.size() == 1) {
declareAttacker(attackerId, defenders.iterator().next(), game);
return true;
}
else {
TargetDefender target = new TargetDefender(defenders, attackerId);
if (chooseTarget(Outcome.Damage, target, null, game)) {
declareAttacker(attackerId, response.getUUID(), game);
return true;
}
}
return false;
}
@ -338,7 +354,7 @@ public class HumanPlayer extends PlayerImpl {
if (response.getBoolean() != null) {
return;
} else if (response.getUUID() != null) {
if (targetCombat.canTarget(playerId, response.getUUID(), game)) {
if (targetCombat.canTarget(playerId, response.getUUID(), null, game)) {
selectCombatGroup(response.getUUID(), game);
}
}
@ -361,7 +377,7 @@ public class HumanPlayer extends PlayerImpl {
int remainingDamage = damage;
while (remainingDamage > 0) {
Target target = new TargetCreatureOrPlayer();
chooseTarget(Outcome.Damage, target, game);
chooseTarget(Outcome.Damage, target, null, game);
if (targets.size() == 0 || targets.contains(target.getFirstTarget())) {
int damageAmount = getAmount(0, remainingDamage, "Select amount", game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
@ -449,4 +465,9 @@ public class HumanPlayer extends PlayerImpl {
}
}
@Override
public HumanPlayer copy() {
return new HumanPlayer(this);
}
}

View file

@ -5,6 +5,8 @@
<playerTypes>
<playerType name="Human" jar="Mage.Player.Human.jar" className="mage.player.human.HumanPlayer"/>
<playerType name="Computer - default" jar="Mage.Player.AI.jar" className="mage.player.ai.ComputerPlayer"/>
<playerType name="Computer - minimax" jar="Mage.Player.AIMinimax.jar" className="mage.player.ai.ComputerPlayer2"/>
<playerType name="Computer - minimax hybrid" jar="Mage.Player.AIMinimax.jar" className="mage.player.ai.ComputerPlayer3"/>
</playerTypes>
<gameTypes>
<gameType name="Two Player Duel" jar="Mage.Game.TwoPlayerDuel.jar" className="mage.game.TwoPlayerDuel" typeName="mage.game.TwoPlayerDuelType"/>

View file

@ -20,6 +20,7 @@ debug.test.classpath=\
dist.dir=dist
dist.jar=${dist.dir}/Mage.Server.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
includes=**
jar.compress=false
@ -27,7 +28,9 @@ javac.classpath=\
${reference.Mage.jar}:\
${reference.Mage_Common.jar}:\
${reference.Mage_Sets.jar}:\
${reference.Mage_Player_AI.jar}
${reference.Mage_Player_AI.jar}:\
${reference.Mage_Player_Human.jar}:\
${reference.Mage_Player_AIMinimax.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
@ -56,15 +59,19 @@ jaxbwiz.xjcrun.classpath=${libs.jaxb.classpath}
main.class=mage.server.Main
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
platform.active=default_platform
platform.active=JDK_1.6_21
project.license=bsd
project.Mage=../Mage
project.Mage_Common=../Mage.Common
project.Mage_Player_AI=../Mage.Player.AI
project.Mage_Player_AIMinimax=../Mage.Player.AIMinimax
project.Mage_Player_Human=../Mage.Player.Human
project.Mage_Sets=../Mage.Sets
reference.Mage.jar=${project.Mage}/dist/Mage.jar
reference.Mage_Common.jar=${project.Mage_Common}/dist/Mage.Common.jar
reference.Mage_Player_AI.jar=${project.Mage_Player_AI}/dist/Mage.Player.AI.jar
reference.Mage_Player_AIMinimax.jar=${project.Mage_Player_AIMinimax}/dist/Mage.Player.AIMinimax.jar
reference.Mage_Player_Human.jar=${project.Mage_Player_Human}/dist/Mage.Player.Human.jar
reference.Mage_Sets.jar=${project.Mage_Sets}/dist/Mage.Sets.jar
run-sys-prop.java.endorsed.dirs=${jaxbwiz.endorsed.dirs}
run.classpath=\

View file

@ -9,6 +9,7 @@
</buildExtensions>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>Mage.Server</name>
<explicit-platform explicit-source-supported="true"/>
<source-roots>
<root id="src.dir"/>
</source-roots>
@ -41,6 +42,22 @@
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
<reference>
<foreign-project>Mage_Player_AIMinimax</foreign-project>
<artifact-type>jar</artifact-type>
<script>build.xml</script>
<target>jar</target>
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
<reference>
<foreign-project>Mage_Player_Human</foreign-project>
<artifact-type>jar</artifact-type>
<script>build.xml</script>
<target>jar</target>
<clean-target>clean</clean-target>
<id>jar</id>
</reference>
<reference>
<foreign-project>Mage_Sets</foreign-project>
<artifact-type>jar</artifact-type>

View file

@ -0,0 +1,7 @@
maxDepth=10
maxNodes=5000
evaluatorLifeFactor=2
evaluatorPermanentFactor=1
evaluatorCreatureFactor=1
evaluatorHandFactor=1
maxThinkSeconds=30

Binary file not shown.

View file

@ -3,13 +3,16 @@
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd">
<server serverAddress="localhost" serverName="mage-server" port="17171" maxGameThreads="10" maxSecondsIdle="600"/>
<playerTypes>
<plugin name="Human" jar="Mage.Player.Human.jar" className="mage.player.human.HumanPlayer"/>
<plugin name="Computer - default" jar="Mage.Player.AI.jar" className="mage.player.ai.ComputerPlayer"/>
<playerType name="Human" jar="Mage.Player.Human.jar" className="mage.player.human.HumanPlayer"/>
<playerType name="Computer - default" jar="Mage.Player.AI.jar" className="mage.player.ai.ComputerPlayer"/>
<playerType name="Computer - minimax" jar="Mage.Player.AIMinimax.jar" className="mage.player.ai.ComputerPlayer2"/>
<playerType name="Computer - minimax hybrid" jar="Mage.Player.AIMinimax.jar" className="mage.player.ai.ComputerPlayer3"/>
</playerTypes>
<gameTypes>
<plugin name="Two Player Duel" jar="Mage.Game.TwoPlayerDuel.jar" className="mage.game.TwoPlayerGame"/>
<gameType name="Two Player Duel" jar="Mage.Game.TwoPlayerDuel.jar" className="mage.game.TwoPlayerDuel" typeName="mage.game.TwoPlayerDuelType"/>
<gameType name="Free For All" jar="Mage.Game.FreeForAll.jar" className="mage.game.FreeForAll" typeName="mage.game.FreeForAllType"/>
</gameTypes>
<deckTypes>
<plugin name="Constructed" jar="Mage.Deck.Constructed.jar" className="mage.deck.Constructed"/>
<deckType name="Constructed" jar="Mage.Deck.Constructed.jar" className="mage.deck.Constructed"/>
</deckTypes>
</config>

View file

@ -30,6 +30,7 @@ package mage.server;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import mage.view.ChatMessage.MessageColor;
/**
*
@ -65,8 +66,8 @@ public class ChatManager {
chatSessions.remove(chatId);
}
public void broadcast(UUID chatId, String userName, String message) {
chatSessions.get(chatId).broadcast(userName, message);
public void broadcast(UUID chatId, String userName, String message, MessageColor color) {
chatSessions.get(chatId).broadcast(userName, message, color);
}
void removeSession(UUID sessionId) {

View file

@ -39,6 +39,7 @@ import java.util.logging.Logger;
import mage.interfaces.callback.ClientCallback;
import mage.util.Logging;
import mage.view.ChatMessage;
import mage.view.ChatMessage.MessageColor;
/**
*
@ -59,7 +60,7 @@ public class ChatSession {
public void join(String userName, UUID sessionId) {
clients.put(sessionId, userName);
broadcast(userName, " has joined");
broadcast(userName, " has joined", MessageColor.BLACK);
logger.info(userName + " joined chat " + chatId);
}
@ -67,18 +68,18 @@ public class ChatSession {
if (clients.containsKey(sessionId)) {
String userName = clients.get(sessionId);
clients.remove(sessionId);
broadcast(userName, " has left");
broadcast(userName, " has left", MessageColor.BLACK);
logger.info(userName + " has left chat " + chatId);
}
}
public void broadcast(String userName, String message) {
public void broadcast(String userName, String message, MessageColor color) {
Calendar cal = new GregorianCalendar();
final String msg = timeFormatter.format(cal.getTime()) + " " + userName + ":" + message;
for (UUID sessionId: clients.keySet()) {
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("chatMessage", new ChatMessage(chatId, msg)));
session.fireCallback(new ClientCallback("chatMessage", new ChatMessage(chatId, msg, color)));
else
kill(sessionId);
}

View file

@ -43,6 +43,7 @@ import mage.cards.decks.DeckCardLists;
import mage.game.GameException;
import mage.interfaces.MageException;
import mage.interfaces.Server;
import mage.interfaces.ServerState;
import mage.interfaces.callback.ClientCallback;
import mage.server.game.DeckValidatorFactory;
import mage.server.game.GameFactory;
@ -52,6 +53,7 @@ import mage.server.game.PlayerFactory;
import mage.server.game.ReplayManager;
import mage.server.game.TableManager;
import mage.util.Logging;
import mage.view.ChatMessage.MessageColor;
import mage.view.GameTypeView;
import mage.view.TableView;
@ -126,9 +128,9 @@ public class ServerImpl extends RemoteServer implements Server {
}
@Override
public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws MageException, GameException {
public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws MageException, GameException {
try {
boolean ret = GamesRoomManager.getInstance().getRoom(roomId).joinTable(sessionId, tableId, seatNum, name, deckList);
boolean ret = GamesRoomManager.getInstance().getRoom(roomId).joinTable(sessionId, tableId, name, deckList);
logger.info("Session " + sessionId + " joined table " + tableId);
return ret;
}
@ -186,7 +188,7 @@ public class ServerImpl extends RemoteServer implements Server {
@Override
public void sendChatMessage(UUID chatId, String userName, String message) throws MageException {
try {
ChatManager.getInstance().broadcast(chatId, userName, message);
ChatManager.getInstance().broadcast(chatId, userName, message, MessageColor.BLUE);
}
catch (Exception ex) {
handleException(ex);
@ -431,9 +433,13 @@ public class ServerImpl extends RemoteServer implements Server {
}
@Override
public List<GameTypeView> getGameTypes() throws MageException {
public ServerState getServerState() throws RemoteException, MageException {
try {
return GameFactory.getInstance().getGameTypes();
return new ServerState(
GameFactory.getInstance().getGameTypes(),
PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]),
DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]),
testMode);
}
catch (Exception ex) {
handleException(ex);
@ -441,33 +447,44 @@ public class ServerImpl extends RemoteServer implements Server {
return null;
}
@Override
public String[] getPlayerTypes() throws MageException {
try {
return PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]);
}
catch (Exception ex) {
handleException(ex);
}
return null;
}
// @Override
// public List<GameTypeView> getGameTypes() throws MageException {
// try {
// return GameFactory.getInstance().getGameTypes();
// }
// catch (Exception ex) {
// handleException(ex);
// }
// return null;
// }
//
// @Override
// public String[] getPlayerTypes() throws MageException {
// try {
// return PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]);
// }
// catch (Exception ex) {
// handleException(ex);
// }
// return null;
// }
//
// @Override
// public String[] getDeckTypes() throws MageException {
// try {
// return DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]);
// }
// catch (Exception ex) {
// handleException(ex);
// }
// return null;
// }
@Override
public String[] getDeckTypes() throws MageException {
try {
return DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]);
}
catch (Exception ex) {
handleException(ex);
}
return null;
}
@Override
public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) throws MageException {
public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) throws MageException {
try {
if (testMode)
GameManager.getInstance().cheat(gameId, sessionId, deckList);
GameManager.getInstance().cheat(gameId, sessionId, playerId, deckList);
}
catch (Exception ex) {
handleException(ex);

View file

@ -77,10 +77,11 @@ public class Session {
return null;
}
public void fireCallback(ClientCallback call) {
public synchronized void fireCallback(ClientCallback call) {
try {
call.setMessageId(messageId++);
logger.info(sessionId + " - " + call.getMessageId() + " - " + call.getMethod());
if (logger.isLoggable(Level.FINE))
logger.fine(sessionId + " - " + call.getMessageId() + " - " + call.getMethod());
callback.setCallback(call);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);

View file

@ -50,6 +50,7 @@ import mage.players.Player;
import mage.util.Logging;
import mage.view.AbilityPickerView;
import mage.view.CardsView;
import mage.view.ChatMessage.MessageColor;
import mage.view.GameView;
/**
@ -90,7 +91,7 @@ public class GameController implements GameCallback {
updateGame();
break;
case INFO:
ChatManager.getInstance().broadcast(chatId, "", event.getMessage());
ChatManager.getInstance().broadcast(chatId, "", event.getMessage(), MessageColor.BLACK);
logger.finest(game.getId() + " " + event.getMessage());
break;
case REVEAL:
@ -152,7 +153,7 @@ public class GameController implements GameCallback {
gameSessions.put(playerId, gameSession);
logger.info("player " + playerId + " has joined game " + game.getId());
// gameSession.init(getGameView(playerId));
ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game");
ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game", MessageColor.BLACK);
if (allJoined()) {
startGame();
}
@ -181,12 +182,12 @@ public class GameController implements GameCallback {
GameWatcher gameWatcher = new GameWatcher(sessionId, game.getId());
watchers.put(sessionId, gameWatcher);
gameWatcher.init(getGameView());
ChatManager.getInstance().broadcast(chatId, "", " has started watching");
ChatManager.getInstance().broadcast(chatId, "", " has started watching", MessageColor.BLACK);
}
public void stopWatching(UUID sessionId) {
watchers.remove(sessionId);
ChatManager.getInstance().broadcast(chatId, "", " has stopped watching");
ChatManager.getInstance().broadcast(chatId, "", " has stopped watching", MessageColor.BLACK);
}
public void concede(UUID sessionId) {
@ -197,11 +198,11 @@ public class GameController implements GameCallback {
game.quit(getPlayerId(sessionId));
}
public void cheat(UUID sessionId, DeckCardLists deckList) {
Player player = game.getPlayer(getPlayerId(sessionId));
public void cheat(UUID sessionId, UUID playerId, DeckCardLists deckList) {
Player player = game.getPlayer(playerId);
Deck deck = Deck.load(deckList);
deck.setOwnerId(player.getId());
for (Card card: deck.getCards().values()) {
game.loadCards(deck.getCards(), playerId);
for (Card card: deck.getCards()) {
player.putOntoBattlefield(card, game);
}
updateGame();
@ -222,7 +223,7 @@ public class GameController implements GameCallback {
public void timeout(UUID sessionId) {
if (sessionPlayerMap.containsKey(sessionId)) {
ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(sessionPlayerMap.get(sessionId)).getName() + " has timed out. Auto concede.");
ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(sessionPlayerMap.get(sessionId)).getName() + " has timed out. Auto concede.", MessageColor.BLACK);
concede(sessionId);
}
}
@ -272,68 +273,72 @@ public class GameController implements GameCallback {
}
private synchronized void ask(UUID playerId, String question) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).ask(question, getGameView(playerId));
informOthers(playerId);
}
private synchronized void chooseAbility(UUID playerId, Collection<? extends Ability> choices) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).chooseAbility(new AbilityPickerView(choices));
informOthers(playerId);
}
private synchronized void choose(UUID playerId, String message, String[] choices) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).choose(message, choices);
informOthers(playerId);
}
private synchronized void target(UUID playerId, String question, Cards cards, boolean required) {
if (gameSessions.containsKey(playerId)) {
if (cards != null)
gameSessions.get(playerId).target(question, new CardsView(cards.getCards(game)), required, getGameView(playerId));
else
gameSessions.get(playerId).target(question, new CardsView(), required, getGameView(playerId));
}
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).target(question, new CardsView(cards), required, getGameView(playerId));
}
private synchronized void target(UUID playerId, String question, Collection<? extends Ability> abilities, boolean required) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).target(question, new CardsView(abilities, game), required, getGameView(playerId));
gameSessions.get(playerId).target(question, new CardsView(abilities, game.getState()), required, getGameView(playerId));
informOthers(playerId);
}
private synchronized void select(UUID playerId, String message) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).select(message, getGameView(playerId));
informOthers(playerId);
}
private synchronized void playMana(UUID playerId, String message) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).playMana(message, getGameView(playerId));
informOthers(playerId);
}
private synchronized void playXMana(UUID playerId, String message) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).playXMana(message, getGameView(playerId));
informOthers(playerId);
}
private synchronized void amount(UUID playerId, String message, int min, int max) {
informOthers(playerId);
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).getAmount(message, min, max);
informOthers(playerId);
}
private synchronized void revealCards(String name, Cards cards) {
for (GameSession session: gameSessions.values()) {
session.revealCards(name, new CardsView(cards));
session.revealCards(name, new CardsView(cards.getCards(game)));
}
}
private synchronized void lookAtCards(UUID playerId, String name, Cards cards) {
if (gameSessions.containsKey(playerId))
gameSessions.get(playerId).revealCards(name, new CardsView(cards));
gameSessions.get(playerId).revealCards(name, new CardsView(cards.getCards(game)));
}
private void informOthers(UUID playerId) {
@ -349,12 +354,12 @@ public class GameController implements GameCallback {
}
private GameView getGameView() {
return new GameView(game.getState());
return new GameView(game.getState(), game);
}
private GameView getGameView(UUID playerId) {
GameView gameView = new GameView(game.getState());
gameView.setHand(new CardsView(game.getPlayer(playerId).getHand()));
GameView gameView = new GameView(game.getState(), game);
gameView.setHand(new CardsView(game.getPlayer(playerId).getHand().getCards(game)));
return gameView;
}

View file

@ -108,8 +108,8 @@ public class GameManager {
// return gameControllers.get(gameId).createReplay();
// }
public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) {
gameControllers.get(gameId).cheat(sessionId, deckList);
public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) {
gameControllers.get(gameId).cheat(sessionId, playerId, deckList);
}
void timeout(UUID gameId, UUID sessionId) {

View file

@ -37,10 +37,12 @@ import mage.game.*;
public class GameReplay {
private GameStates savedGame;
private Game game;
private int stateIndex;
public GameReplay(GameStates savedGame) {
this.savedGame = savedGame;
public GameReplay(Game game) {
this.savedGame = game.getGameStates();
this.game = game;
}
public void start() {
@ -61,4 +63,7 @@ public class GameReplay {
return null;
}
public Game getGame() {
return this.game;
}
}

View file

@ -63,6 +63,7 @@ public class GameSession extends GameWatcher {
public void ask(final String question, final GameView gameView) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameAsk", new GameClientMessage(gameView, question)));
@ -71,6 +72,7 @@ public class GameSession extends GameWatcher {
public void target(final String question, final CardsView cardView, final boolean required, final GameView gameView) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameTarget", new GameClientMessage(gameView, question, cardView, required)));
@ -79,6 +81,7 @@ public class GameSession extends GameWatcher {
public void select(final String message, final GameView gameView) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameSelect", new GameClientMessage(gameView, message)));
@ -87,6 +90,7 @@ public class GameSession extends GameWatcher {
public void chooseAbility(final AbilityPickerView abilities) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameChooseAbility", abilities));
@ -95,6 +99,7 @@ public class GameSession extends GameWatcher {
public void choose(final String message, final String[] choices) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameChoose", new GameClientMessage(choices, message)));
@ -103,6 +108,7 @@ public class GameSession extends GameWatcher {
public void playMana(final String message, final GameView gameView) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gamePlayMana", new GameClientMessage(gameView, message)));
@ -111,6 +117,7 @@ public class GameSession extends GameWatcher {
public void playXMana(final String message, final GameView gameView) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gamePlayXMana", new GameClientMessage(gameView, message)));
@ -119,6 +126,7 @@ public class GameSession extends GameWatcher {
public void getAmount(final String message, final int min, final int max) {
if (!killed) {
setupTimeout();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("gameSelectAmount", new GameClientMessage(message, min, max)));

View file

@ -43,7 +43,7 @@ import mage.view.TableView;
public interface GamesRoom extends Room {
public List<TableView> getTables();
public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException;
public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException;
public TableView createTable(UUID sessionId, String gameType, String deckType, List<String> playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range);
public void removeTable(UUID sessionId, UUID tableId);
public TableView getTable(UUID tableId);

View file

@ -62,9 +62,9 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
}
@Override
public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException {
public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException {
if (tables.containsKey(tableId)) {
return TableManager.getInstance().joinTable(sessionId, tableId, seatNum, name, deckList);
return TableManager.getInstance().joinTable(sessionId, tableId, name, deckList);
} else {
return false;
}

View file

@ -30,6 +30,7 @@ package mage.server.game;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
@ -48,7 +49,7 @@ public class PlayerFactory {
private final static PlayerFactory INSTANCE = new PlayerFactory();
private final static Logger logger = Logging.getLogger(PlayerFactory.class.getName());
private Map<String, Class> playerTypes = new HashMap<String, Class>();
private Map<String, Class> playerTypes = new LinkedHashMap<String, Class>();
public static PlayerFactory getInstance() {
return INSTANCE;

View file

@ -30,6 +30,7 @@ package mage.server.game;
import java.util.UUID;
import java.util.logging.Logger;
import mage.game.Game;
import mage.game.GameState;
import mage.interfaces.callback.ClientCallback;
import mage.server.Session;
@ -45,19 +46,19 @@ public class ReplaySession implements GameCallback {
private final static Logger logger = Logging.getLogger(ReplaySession.class.getName());
private GameReplay game;
private GameReplay replay;
protected UUID sessionId;
ReplaySession(UUID tableId, UUID sessionId) {
this.game = TableManager.getInstance().createReplay(tableId);
this.replay = TableManager.getInstance().createReplay(tableId);
this.sessionId = sessionId;
}
public void replay() {
game.start();
replay.start();
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("replayInit", new GameView(game.next())));
session.fireCallback(new ClientCallback("replayInit", new GameView(replay.next(), replay.getGame())));
}
public void stop() {
@ -65,11 +66,11 @@ public class ReplaySession implements GameCallback {
}
public synchronized void next() {
updateGame(game.next());
updateGame(replay.next(), replay.getGame());
}
public synchronized void previous() {
updateGame(game.previous());
updateGame(replay.previous(), replay.getGame());
}
@Override
@ -79,14 +80,14 @@ public class ReplaySession implements GameCallback {
session.fireCallback(new ClientCallback("replayDone", result));
}
private void updateGame(final GameState state) {
private void updateGame(final GameState state, Game game) {
if (state == null) {
gameResult("game ended");
}
else {
Session session = SessionManager.getInstance().getSession(sessionId);
if (session != null)
session.fireCallback(new ClientCallback("replayUpdate", new GameView(state)));
session.fireCallback(new ClientCallback("replayUpdate", new GameView(state, game)));
}
}

View file

@ -86,21 +86,26 @@ public class TableController {
table = new Table(gameType, DeckValidatorFactory.getInstance().createDeckValidator(deckType), playerTypes);
}
public synchronized boolean joinTable(UUID sessionId, int seatNum, String name, DeckCardLists deckList) throws GameException {
public synchronized boolean joinTable(UUID sessionId, String name, DeckCardLists deckList) throws GameException {
if (table.getState() != TableState.WAITING) {
return false;
}
Seat seat = table.getSeats()[seatNum];
Seat seat = table.getNextAvailableSeat();
if (seat == null) {
throw new GameException("No available seats.");
}
Deck deck = Deck.load(deckList);
if (!Main.server.isTestMode() && !validDeck(deck)) {
throw new GameException(name + " has an invalid deck for this format");
}
Player player = createPlayer(name, deck, seat.getPlayerType());
table.joinTable(player, seatNum);
game.loadCards(deck.getCards(), player.getId());
game.loadCards(deck.getSideboard(), player.getId());
table.joinTable(player, seat);
logger.info("player joined " + player.getId());
//only add human players to sessionPlayerMap
if (table.getSeats()[seatNum].getPlayer().isHuman()) {
if (seat.getPlayer().isHuman()) {
sessionPlayerMap.put(sessionId, player.getId());
}
@ -183,11 +188,11 @@ public class TableController {
private void saveGame() {
try {
//use buffering
OutputStream file = new FileOutputStream("saved/" + game.getId().toString() + ".game");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(new GZIPOutputStream(buffer));
try {
output.writeObject(game);
output.writeObject(game.getGameStates());
}
finally {
@ -200,17 +205,16 @@ public class TableController {
}
}
private GameStates loadGame() {
private Game loadGame() {
try{
//use buffering
InputStream file = new FileInputStream("saved/" + gameId.toString() + ".game");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new CopierObjectInputStream(Main.classLoader, new GZIPInputStream(buffer));
//ObjectInput input = new ObjectInputStream(buffer);
try {
//deserialize the List
GameStates gameStates = (GameStates)input.readObject();
return gameStates;
Game game = (Game)input.readObject();
GameStates states = (GameStates)input.readObject();
game.loadGameStates(states);
return game;
}
finally {
input.close();

View file

@ -71,8 +71,8 @@ public class TableManager {
return tables.values();
}
public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException {
return controllers.get(tableId).joinTable(sessionId, seatNum, name, deckList);
public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException {
return controllers.get(tableId).joinTable(sessionId, name, deckList);
}
public void removeSession(UUID sessionId) {

View file

@ -0,0 +1,102 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets;
import mage.sets.magic2011.*;
import mage.cards.ExpansionSet;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class Magic2011 extends ExpansionSet {
private static final Magic2011 fINSTANCE = new Magic2011();
public static Magic2011 getInstance() {
return fINSTANCE;
}
private Magic2011() {
this.name = "Magic 2011";
this.cards.add(AcidicSlime.class);
this.cards.add(ActOfTreason.class);
this.cards.add(AetherAdept.class);
this.cards.add(AirServant.class);
this.cards.add(AjaniGoldmane.class);
// this.cards.add(AlluringSiren.class);
// this.cards.add(AngelsFeather.class);
// this.cards.add(AngelsMercy.class);
// this.cards.add(AntQueen.class);
// this.cards.add(ArmoredAscension.class);
// this.cards.add(Assassinate.class);
// this.cards.add(AwakenerDruid.class);
// this.cards.add(BallLightning.class);
// this.cards.add(BaneslayerAngel.class);
// this.cards.add(BirdsOfParadise.class);
// this.cards.add(BerserkersOfBloodRidge.class);
// this.cards.add(BlackKnight.class);
// this.cards.add(BlindingMage.class);
// this.cards.add(BogWraith.class);
// this.cards.add(BogardanHellkite.class);
// this.cards.add(Cancel.class);
// this.cards.add(CelestialPurge.class);
// this.cards.add(DiabolicTutor.class);
// this.cards.add(DoomBlade.class);
// this.cards.add(DragonskullSummit.class);
// this.cards.add(Earthquake.class);
// this.cards.add(EliteVanguard.class);
// this.cards.add(Flashfreeze.class);
// this.cards.add(GargoyleCastle.class);
// this.cards.add(GarrukWildspeaker.class);
// this.cards.add(GiantGrowth.class);
// this.cards.add(GlacialFortress.class);
// this.cards.add(GreatSableStag.class);
// this.cards.add(HonorOfThePure.class);
// this.cards.add(HowlingMine.class);
// this.cards.add(JaceBeleren.class);
// this.cards.add(LlanowarElves.class);
// this.cards.add(LightningBolt.class);
// this.cards.add(MasterOfTheWildHunt.class);
// this.cards.add(MightOfOaks.class);
// this.cards.add(MindRot.class);
// this.cards.add(Naturalize.class);
// this.cards.add(Overrun.class);
// this.cards.add(RampantGrowth.class);
// this.cards.add(MindSpring.class);
// this.cards.add(Negate.class);
// this.cards.add(RootboundCrag.class);
// this.cards.add(RoyalAssassin.class);
// this.cards.add(SafePassage.class);
// this.cards.add(SunpetalGrove.class);
// this.cards.add(TerramorphicExpanse.class);
// this.cards.add(TimeWarp.class);
// this.cards.add(WhiteKnight.class);
}
}

View file

@ -47,7 +47,11 @@ public class RiseOfTheEldrazi extends ExpansionSet {
this.name = "Rise Of The Eldrazi";
this.cards.add(Deprive.class);
this.cards.add(GideonJura.class);
this.cards.add(JoragaTreespeaker.class);
this.cards.add(KarganDragonlord.class);
this.cards.add(KozileksPredator.class);
this.cards.add(NestInvader.class);
this.cards.add(Vengevine.class);
this.cards.add(WallOfOmens.class);
}

View file

@ -47,6 +47,7 @@ public class Sets extends ArrayList<ExpansionSet> {
this.add(AlaraReborn.getInstance());
this.add(Conflux.getInstance());
this.add(Magic2010.getInstance());
this.add(Magic2011.getInstance());
this.add(Planechase.getInstance());
this.add(RiseOfTheEldrazi.getInstance());
this.add(ShardsOfAlara.getInstance());

View file

@ -45,10 +45,13 @@ public class Worldwake extends ExpansionSet {
private Worldwake() {
this.name = "Worldwake";
this.cards.add(ArborElf.class);
this.cards.add(BasiliskCollar.class);
this.cards.add(CelestialColonnade.class);
this.cards.add(DreadStatuary.class);
this.cards.add(EverflowingChalice.class);
this.cards.add(JaceTheMindSculptor.class);
this.cards.add(KhalniGarden.class);
this.cards.add(LavaclawReaches.class);
this.cards.add(RagingRavine.class);
this.cards.add(SearingBlaze.class);
@ -56,6 +59,7 @@ public class Worldwake extends ExpansionSet {
this.cards.add(StirringWildwood.class);
this.cards.add(StoneforgeMystic.class);
this.cards.add(TectonicEdge.class);
this.cards.add(WolfbriarElemental.class);
}
}

View file

@ -45,12 +45,16 @@ public class Zendikar extends ExpansionSet {
private Zendikar() {
this.name = "Zendikar";
this.cards.add(AdventuringGear.class);
this.cards.add(AetherFigment.class);
this.cards.add(ArchiveTrap.class);
this.cards.add(AridMesa.class);
this.cards.add(BeastmasterAscension.class);
this.cards.add(BraveTheElements.class);
this.cards.add(BurstLightning.class);
this.cards.add(ConquerorsPledge.class);
this.cards.add(DayOfJudgment.class);
this.cards.add(EldraziMonument.class);
this.cards.add(EmeriaAngel.class);
this.cards.add(GoblinGuide.class);
this.cards.add(GoblinRuinblaster.class);
@ -60,6 +64,7 @@ public class Zendikar extends ExpansionSet {
this.cards.add(MistyRainforest.class);
this.cards.add(OranRiefTheVastwood.class);
this.cards.add(RampagingBaloths.class);
this.cards.add(RiverBoa.class);
this.cards.add(ScaldingTarn.class);
this.cards.add(ScuteMob.class);
this.cards.add(SpreadingSeas.class);

View file

@ -32,6 +32,7 @@ import java.util.UUID;
import mage.Constants.CardType;
import mage.Constants.Outcome;
import mage.Constants.Zone;
import mage.MageObjectImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.BoostEquippedEffect;
@ -46,12 +47,11 @@ import mage.sets.AlaraReborn;
*
* @author BetaSteward_at_googlemail.com
*/
public class BehemothSledge extends CardImpl {
public class BehemothSledge extends CardImpl<BehemothSledge> {
public BehemothSledge(UUID ownerId) {
super(ownerId, "Behemoth Sledge", new CardType[]{CardType.ARTIFACT}, "{1}{G}{W}");
this.expansionSetId = AlaraReborn.getInstance().getId();
this.art = "120963_typ_reg_sty_010.jpg";
this.subtype.add("Equipment");
this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(LifelinkAbility.getInstance())));
@ -59,4 +59,18 @@ public class BehemothSledge extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2)));
}
protected BehemothSledge(BehemothSledge me) {
super(me);
}
@Override
public BehemothSledge copy() {
return new BehemothSledge(this);
}
@Override
public String getArt() {
return "120963_typ_reg_sty_010.jpg";
}
}

View file

@ -40,17 +40,30 @@ import mage.target.common.TargetCreaturePermanent;
*
* @author BetaSteward_at_googlemail.com
*/
public class BituminousBlast extends CardImpl {
public class BituminousBlast extends CardImpl<BituminousBlast> {
public BituminousBlast(UUID ownerId) {
super(ownerId, "Bituminous Blast", new CardType[]{CardType.INSTANT}, "{3}{B}{R}");
this.expansionSetId = AlaraReborn.getInstance().getId();
this.color.setBlack(true);
this.color.setRed(true);
this.art = "121045_typ_reg_sty_010.jpg";
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
this.addAbility(new CascadeAbility());
}
public BituminousBlast(final BituminousBlast card) {
super(card);
}
@Override
public BituminousBlast copy() {
return new BituminousBlast(this);
}
@Override
public String getArt() {
return "121045_typ_reg_sty_010.jpg";
}
}

View file

@ -40,7 +40,7 @@ import mage.sets.AlaraReborn;
*
* @author BetaSteward_at_googlemail.com
*/
public class BloodbraidElf extends CardImpl {
public class BloodbraidElf extends CardImpl<BloodbraidElf> {
public BloodbraidElf(UUID ownerId) {
super(ownerId, "Bloodbraid Elf", new CardType[]{CardType.CREATURE}, "{2}{R}{G}");
@ -49,10 +49,23 @@ public class BloodbraidElf extends CardImpl {
this.color.setGreen(true);
this.subtype.add("Elf");
this.subtype.add("Berserker");
this.art = "121042_typ_reg_sty_010.jpg";
this.power = new MageInt(3);
this.toughness = new MageInt(2);
this.addAbility(HasteAbility.getInstance());
this.addAbility(new CascadeAbility());
}
}
public BloodbraidElf(final BloodbraidElf card) {
super(card);
}
@Override
public BloodbraidElf copy() {
return new BloodbraidElf(this);
}
@Override
public String getArt() {
return "121042_typ_reg_sty_010.jpg";
}
}

Some files were not shown because too many files have changed in this diff Show more