diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form
index 8a9d385eae..6f93568ea0 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form
@@ -59,6 +59,10 @@
+
+
+
+
@@ -133,6 +137,8 @@
+
+
@@ -389,5 +395,12 @@
+
+
+
+
+
+
+
diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
index 2e41a98513..95158581c3 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
@@ -79,6 +79,7 @@ public class NewTableDialog extends MageDialog {
this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1));
this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1));
this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5));
+ this.spnEdhPowerLevel.setModel(new SpinnerNumberModel(100, 0, 100, 5));
MageFrame.getUI().addButton(MageComponents.NEW_TABLE_OK_BUTTON, btnOK);
}
@@ -125,7 +126,9 @@ public class NewTableDialog extends MageDialog {
btnOK = new javax.swing.JButton();
btnCancel = new javax.swing.JButton();
lblQuitRatio = new javax.swing.JLabel();
+ lblEdhPowerLevel = new javax.swing.JLabel();
spnQuitRatio = new javax.swing.JSpinner();
+ spnEdhPowerLevel = new javax.swing.JSpinner();
setTitle("New Table");
@@ -214,8 +217,10 @@ public class NewTableDialog extends MageDialog {
});
lblQuitRatio.setText("Allowed quit %");
+ lblEdhPowerLevel.setText("EDH power level");
spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table");
+ spnEdhPowerLevel.setToolTipText("Players with decks with a higher power level can't join this table");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
@@ -256,7 +261,10 @@ public class NewTableDialog extends MageDialog {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lblQuitRatio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(lblEdhPowerLevel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
@@ -314,7 +322,10 @@ public class NewTableDialog extends MageDialog {
.addComponent(lbDeckType)
.addComponent(lblQuitRatio)
.addComponent(chkRated)
- .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(lblEdhPowerLevel)
+ .addComponent(chkRated)
+ .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@@ -395,6 +406,7 @@ public class NewTableDialog extends MageDialog {
options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue());
options.setPassword(this.txtPassword.getText());
options.setQuitRatio((Integer) this.spnQuitRatio.getValue());
+ options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue());
if (!checkMatchOptions(options)) {
return;
}
@@ -658,6 +670,7 @@ public class NewTableDialog extends MageDialog {
}
this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO, "100")));
+ this.spnEdhPowerLevel.setValue(0);
}
/**
@@ -721,6 +734,7 @@ public class NewTableDialog extends MageDialog {
private javax.swing.JLabel lblNumWins;
private javax.swing.JLabel lblPassword;
private javax.swing.JLabel lblQuitRatio;
+ private javax.swing.JLabel lblEdhPowerLevel;
private javax.swing.JLabel lblRange;
private javax.swing.JLabel lblSkillLevel;
private mage.client.table.NewPlayerPanel player1Panel;
@@ -729,6 +743,7 @@ public class NewTableDialog extends MageDialog {
private javax.swing.JSpinner spnNumPlayers;
private javax.swing.JSpinner spnNumWins;
private javax.swing.JSpinner spnQuitRatio;
+ private javax.swing.JSpinner spnEdhPowerLevel;
private javax.swing.JTextField txtName;
private javax.swing.JTextField txtPassword;
// End of variables declaration//GEN-END:variables
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java
index 6fef4d52ed..11fa3fbc50 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java
@@ -204,4 +204,274 @@ public class Commander extends Constructed {
|| cardColor.isWhite() && !commander.isWhite());
}
+ @Override
+ public int getEdhPowerLevel(Deck deck) {
+ if (deck == null) {
+ return 0;
+ }
+
+ int edhPowerLevel = 0;
+ for (Card card : deck.getCards()) {
+
+ int thisMaxPower = 0;
+
+ // Examine rules to work out most egregious functions in edh
+ boolean anyNumberOfTarget = false;
+ boolean buyback = false;
+ boolean cascade = false;
+ boolean copy = false;
+ boolean exile = false;
+ boolean exileAll = false;
+ boolean counter = false;
+ boolean destroy = false;
+ boolean destroyAll = false;
+ boolean each = false;
+ boolean exalted = false;
+ boolean drawCards = false;
+ boolean extraTurns = false;
+ boolean gainControl = false;
+ boolean infect = false;
+ boolean mayCastForFree = false;
+ boolean overload = false;
+ boolean persist = false;
+ boolean proliferate = false;
+ boolean retrace = false;
+ boolean sacrifice = false;
+ boolean skip = false;
+ boolean sliver = false;
+ boolean tutor = false;
+ boolean undying = false;
+ boolean wheneverEnters = false;
+ boolean youControlTarget = false;
+
+ for (String str : card.getRules()) {
+ String s = str.toLowerCase();
+ anyNumberOfTarget |= s.contains("any number");
+ buyback |= s.contains("buyback");
+ cascade |= s.contains("cascade");
+ copy |= s.contains("copy");
+ counter |= s.contains("counter") && s.contains("target");
+ destroy |= s.contains("destroy");
+ destroyAll |= s.contains("destroy all");
+ drawCards |= s.contains("draw cards");
+ each |= s.contains("each");
+ exalted |= s.contains("exalted");
+ exile |= s.contains("exile");
+ exileAll |= s.contains("exile") && s.contains(" all ");
+ extraTurns |= s.contains("extra turn");
+ gainControl |= s.contains("gain control");
+ infect |= s.contains("infect");
+ mayCastForFree |= s.contains("may cast") && s.contains("without paying");
+ overload |= s.contains("overload");
+ persist |= s.contains("persist");
+ proliferate |= s.contains("proliferate");
+ retrace |= s.contains("retrace");
+ sacrifice |= s.contains("sacrifice");
+ skip |= s.contains("skip") && s.contains("each");
+ sliver |= s.contains("sliver");
+ tutor |= s.contains("search your library");
+ undying |= s.contains("undying");
+ wheneverEnters |= s.contains("when") && s.contains("another") && s.contains("enters");
+ youControlTarget |= s.contains("you control target");
+ }
+
+ if (extraTurns) {
+ thisMaxPower = Math.max(thisMaxPower, 7);
+ }
+ if (buyback) {
+ thisMaxPower = Math.max(thisMaxPower, 6);
+ }
+ if (tutor) {
+ thisMaxPower = Math.max(thisMaxPower, 6);
+ }
+ if (infect) {
+ thisMaxPower = Math.max(thisMaxPower, 5);
+ }
+ if (overload) {
+ thisMaxPower = Math.max(thisMaxPower, 5);
+ }
+ if (cascade) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (each) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (exileAll) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (gainControl) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (mayCastForFree) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (proliferate) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (skip) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (wheneverEnters) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (youControlTarget) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+ if (anyNumberOfTarget) {
+ thisMaxPower = Math.max(thisMaxPower, 3);
+ }
+ if (destroyAll) {
+ thisMaxPower = Math.max(thisMaxPower, 3);
+ }
+ if (undying) {
+ thisMaxPower = Math.max(thisMaxPower, 3);
+ }
+ if (persist) {
+ thisMaxPower = Math.max(thisMaxPower, 3);
+ }
+ if (exile) {
+ thisMaxPower = Math.max(thisMaxPower, 2);
+ }
+ if (sliver) {
+ thisMaxPower = Math.max(thisMaxPower, 2);
+ }
+ if (sacrifice) {
+ thisMaxPower = Math.max(thisMaxPower, 2);
+ }
+ if (copy) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+ if (counter) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+ if (destroy) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+ if (drawCards) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+ if (exalted) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+ if (retrace) {
+ thisMaxPower = Math.max(thisMaxPower, 1);
+ }
+
+ // Planeswalkers
+ if (card.getCardType().contains(CardType.PLANESWALKER)) {
+ if (card.getName().toLowerCase().equals("jace, the mind sculptor")) {
+ thisMaxPower = Math.max(thisMaxPower, 5);
+ }
+ thisMaxPower = Math.max(thisMaxPower, 3);
+ }
+
+ if (card.getCardType().contains(CardType.LAND)) {
+ thisMaxPower = 0;
+ }
+
+ // Banned in french
+ String cn = card.getName().toLowerCase();
+ if (cn.equals("ancient tomb") || cn.equals("armageddon")
+ || cn.equals("aura shards") || cn.equals("back to basics")
+ || cn.equals("bane of progress") || cn.equals("basalt monolith")
+ || cn.equals("blightsteel collossus") || cn.equals("cabal coffers")
+ || cn.equals("craterhoof behemoth") || cn.equals("deepglow skate")
+ || cn.equals("dig through time") || cn.equals("entomb")
+ || cn.equals("food chain") || cn.equals("gaea's cradle")
+ || cn.equals("grim monolith") || cn.equals("hermit druid")
+ || cn.equals("humility") || cn.equals("imperial seal")
+ || cn.equals("karakas") || cn.equals("living death")
+ || cn.equals("loyal retainers") || cn.equals("mana crypt")
+ || cn.equals("mana drain") || cn.equals("mana vault")
+ || cn.equals("necrotic ooze") || cn.equals("oath of druids")
+ || cn.equals("protean hulk") || cn.equals("ravages of war")
+ || cn.equals("reclamation sage") || cn.equals("sensei's divning top")
+ || cn.equals("sol ring") || cn.equals("spore frog")
+ || cn.equals("strip mine") || cn.equals("the tabernacle at pendrell vale")
+ || cn.equals("tinker") || cn.equals("tolarian academy")
+ || cn.equals("winter orb") || cn.equals("treasure cruise")) {
+ thisMaxPower = Math.max(thisMaxPower, 4);
+ }
+
+ // Parts of infinite combos
+ if (cn.equals("animate artifact") || cn.equals("archaeomancer")
+ || cn.equals("ashnod's altar") || cn.equals("azami, lady of scrolls")
+ || cn.equals("basalt monolith") || cn.equals("brago, king eternal")
+ || cn.equals("candelabra of tawnos") || cn.equals("cephalid aristocrat")
+ || cn.equals("cephalid illusionist") || cn.equals("changeling berserker")
+ || cn.equals("cinderhaze wretch") || cn.equals("cryptic gateway")
+ || cn.equals("deadeye navigator") || cn.equals("derevi, empyrial tactician")
+ || cn.equals("doubling season") || cn.equals("dross scorpion")
+ || cn.equals("earthcraft") || cn.equals("erratic portal")
+ || cn.equals("enter the infinite") || cn.equals("omniscience")
+ || cn.equals("exquisite blood") || cn.equals("future sight")
+ || cn.equals("grave titan") || cn.equals("great whale")
+ || cn.equals("grim monolith") || cn.equals("gush")
+ || cn.equals("hellkite charger") || cn.equals("intruder alarm")
+ || cn.equals("iona, shield of emeria")
+ || cn.equals("karn, silver golem") || cn.equals("kiki-jiki, mirror breaker")
+ || cn.equals("krark-clan ironworks") || cn.equals("krenko, mob boss")
+ || cn.equals("krosan restorer") || cn.equals("laboratory maniac")
+ || cn.equals("leovold, emissary of trest")
+ || cn.equals("leonin relic-warder") || cn.equals("leyline of the void")
+ || cn.equals("memnarch") || cn.equals("memnarch")
+ || cn.equals("meren of clan nel toth") || cn.equals("mikaeus, the unhallowed")
+ || cn.equals("mindcrank") || cn.equals("mindslaver")
+ || cn.equals("minion reflector") || cn.equals("mycosynth lattice")
+ || cn.equals("myr turbine") || cn.equals("narset, enlightened master")
+ || cn.equals("nekusar, the mindrazer") || cn.equals("norin the wary")
+ || cn.equals("opalescence") || cn.equals("ornithopter")
+ || cn.equals("planar portal") || cn.equals("power artifact")
+ || cn.equals("rings of brighthearth") || cn.equals("rite of replication")
+ || cn.equals("sanguine bond") || cn.equals("sensei's divining top")
+ || cn.equals("splinter twin") || cn.equals("stony silence")
+ || cn.equals("storm cauldron") || cn.equals("teferi's puzzle box")
+ || cn.equals("teferi, mage of zhalfir") || cn.equals("teferi, mage of zhalfir")
+ || cn.equals("tezzeret the seeker") || cn.equals("time stretch")
+ || cn.equals("time warp") || cn.equals("training grounds")
+ || cn.equals("triskelavus") || cn.equals("triskelion")
+ || cn.equals("turnabout") || cn.equals("umbral mantle")
+ || cn.equals("uyo, silent prophet") || cn.equals("voltaic key")
+ || cn.equals("workhorse") || cn.equals("worldgorger dragon")
+ || cn.equals("worthy cause") || cn.equals("yawgmoth's will")
+ || cn.equals("zealous conscripts")) {
+ thisMaxPower = Math.max(thisMaxPower, 6);
+ }
+ System.out.println(thisMaxPower + "Card:" + cn + " " + thisMaxPower);
+
+ edhPowerLevel += thisMaxPower;
+ }
+
+ for (Card commander : deck.getSideboard()) {
+ int thisMaxPower = 0;
+ String cn = commander.getName().toLowerCase();
+ // Least fun commanders
+ if (cn.equals("memnarch")
+ || cn.equals("derevi, empyrial tactician")
+ || cn.equals("narset, enlightened master")
+ || cn.equals("nekusar, the mindrazer")
+ || cn.equals("norin the wary")) {
+ thisMaxPower = Math.max(thisMaxPower, 15);
+ }
+
+ // Next least fun commanders
+ if (cn.equals("meren of clan nel toth")
+ || cn.equals("teferi, mage of zhalfir")
+ || cn.equals("azami, lady of scrolls")
+ || cn.equals("brago, king eternal")
+ || cn.equals("mikaeus the unhallowed")
+ || cn.equals("memnarch")) {
+ thisMaxPower = Math.max(thisMaxPower, 10);
+ }
+
+ System.out.println(thisMaxPower + "Card:" + cn + " bad commander" + thisMaxPower);
+ edhPowerLevel += thisMaxPower;
+ }
+
+ edhPowerLevel = (int) Math.round(edhPowerLevel / 2.5);
+ if (edhPowerLevel > 100) {
+ edhPowerLevel = 100;
+ }
+ return edhPowerLevel;
+ }
}
diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java
index 2162af3c6b..6b1704182f 100644
--- a/Mage.Server/src/main/java/mage/server/TableController.java
+++ b/Mage.Server/src/main/java/mage/server/TableController.java
@@ -297,6 +297,19 @@ public class TableController {
user.showUserMessage("Join Table", message);
return false;
}
+
+ // Check power level for table (currently only used for EDH/Commander table)
+ int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel();
+ if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase().equals("commander")) {
+ int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck);
+ if (deckEdhPowerLevel > edhPowerLevel) {
+ String message = new StringBuilder("Your deck appears to be too powerful for this table.\n\nReduce the number of extra turn cards, infect, counters, fogs, reconsider your commander. ")
+ .append("\nThe table requirement has a maximum power level of ").append(edhPowerLevel).append (" whilst your deck has a calculated power level of ")
+ .append(deckEdhPowerLevel).toString();
+ user.showUserMessage("Join Table", message);
+ return false;
+ }
+ }
Player player = createPlayer(name, seat.getPlayerType(), skill);
if (player == null) {
diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidator.java b/Mage/src/main/java/mage/cards/decks/DeckValidator.java
index 383e1ad8a0..bc73bc61d9 100644
--- a/Mage/src/main/java/mage/cards/decks/DeckValidator.java
+++ b/Mage/src/main/java/mage/cards/decks/DeckValidator.java
@@ -67,4 +67,8 @@ public abstract class DeckValidator implements Serializable {
}
}
}
+
+ public int getEdhPowerLevel(Deck deck) {
+ return 0;
+ }
}
diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java
index 37901b91be..3bcd909ab8 100644
--- a/Mage/src/main/java/mage/game/match/MatchOptions.java
+++ b/Mage/src/main/java/mage/game/match/MatchOptions.java
@@ -58,6 +58,7 @@ public class MatchOptions implements Serializable {
protected SkillLevel skillLevel;
protected boolean rollbackTurnsAllowed;
protected int quitRatio;
+ protected int edhPowerLevel;
protected boolean rated;
protected int numSeatsForMatch;
@@ -208,6 +209,14 @@ public class MatchOptions implements Serializable {
public void setQuitRatio(int quitRatio) {
this.quitRatio = quitRatio;
}
+
+ public int getEdhPowerLevel() {
+ return edhPowerLevel;
+ }
+
+ public void setEdhPowerLevel(int edhPowerLevel) {
+ this.edhPowerLevel = edhPowerLevel;
+ }
public boolean isRated() {
return rated;