Add 'EDH power level' rating to Commander tables

This commit is contained in:
spjspj 2016-11-20 23:13:16 +11:00
parent 7d60d884c3
commit 7fd2eeedcf
6 changed files with 326 additions and 2 deletions

View file

@ -59,6 +59,10 @@
<Component id="lblQuitRatio" min="-2" max="-2" attributes="0"/> <Component id="lblQuitRatio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="spnQuitRatio" min="-2" pref="60" max="-2" attributes="0"/> <Component id="spnQuitRatio" min="-2" pref="60" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblEdhPowerLevel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="spnEdhPowerLevel" min="-2" pref="60" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group> </Group>
@ -133,6 +137,8 @@
<Component id="lbDeckType" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="lbDeckType" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblQuitRatio" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="lblQuitRatio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spnQuitRatio" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="spnQuitRatio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblEdhPowerLevel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spnEdhPowerLevel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
@ -389,5 +395,12 @@
</Component> </Component>
<Component class="javax.swing.JSpinner" name="spnQuitRatio"> <Component class="javax.swing.JSpinner" name="spnQuitRatio">
</Component> </Component>
<Component class="javax.swing.JLabel" name="lblEdhPowerLevel">
<Properties>
<Property name="text" type="java.lang.String" value="EDH power level"/>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spnEdhPowerLevel">
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View file

@ -79,6 +79,7 @@ public class NewTableDialog extends MageDialog {
this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1)); this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1));
this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1)); this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1));
this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5)); 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); MageFrame.getUI().addButton(MageComponents.NEW_TABLE_OK_BUTTON, btnOK);
} }
@ -125,7 +126,9 @@ public class NewTableDialog extends MageDialog {
btnOK = new javax.swing.JButton(); btnOK = new javax.swing.JButton();
btnCancel = new javax.swing.JButton(); btnCancel = new javax.swing.JButton();
lblQuitRatio = new javax.swing.JLabel(); lblQuitRatio = new javax.swing.JLabel();
lblEdhPowerLevel = new javax.swing.JLabel();
spnQuitRatio = new javax.swing.JSpinner(); spnQuitRatio = new javax.swing.JSpinner();
spnEdhPowerLevel = new javax.swing.JSpinner();
setTitle("New Table"); setTitle("New Table");
@ -214,8 +217,10 @@ public class NewTableDialog extends MageDialog {
}); });
lblQuitRatio.setText("Allowed quit %"); lblQuitRatio.setText("Allowed quit %");
lblEdhPowerLevel.setText("EDH power level");
spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table"); 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()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); getContentPane().setLayout(layout);
@ -256,7 +261,10 @@ public class NewTableDialog extends MageDialog {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lblQuitRatio) .addComponent(lblQuitRatio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .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(jLabel1, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
@ -314,7 +322,10 @@ public class NewTableDialog extends MageDialog {
.addComponent(lbDeckType) .addComponent(lbDeckType)
.addComponent(lblQuitRatio) .addComponent(lblQuitRatio)
.addComponent(chkRated) .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) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@ -395,6 +406,7 @@ public class NewTableDialog extends MageDialog {
options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue()); options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue());
options.setPassword(this.txtPassword.getText()); options.setPassword(this.txtPassword.getText());
options.setQuitRatio((Integer) this.spnQuitRatio.getValue()); options.setQuitRatio((Integer) this.spnQuitRatio.getValue());
options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue());
if (!checkMatchOptions(options)) { if (!checkMatchOptions(options)) {
return; 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.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 lblNumWins;
private javax.swing.JLabel lblPassword; private javax.swing.JLabel lblPassword;
private javax.swing.JLabel lblQuitRatio; private javax.swing.JLabel lblQuitRatio;
private javax.swing.JLabel lblEdhPowerLevel;
private javax.swing.JLabel lblRange; private javax.swing.JLabel lblRange;
private javax.swing.JLabel lblSkillLevel; private javax.swing.JLabel lblSkillLevel;
private mage.client.table.NewPlayerPanel player1Panel; private mage.client.table.NewPlayerPanel player1Panel;
@ -729,6 +743,7 @@ public class NewTableDialog extends MageDialog {
private javax.swing.JSpinner spnNumPlayers; private javax.swing.JSpinner spnNumPlayers;
private javax.swing.JSpinner spnNumWins; private javax.swing.JSpinner spnNumWins;
private javax.swing.JSpinner spnQuitRatio; private javax.swing.JSpinner spnQuitRatio;
private javax.swing.JSpinner spnEdhPowerLevel;
private javax.swing.JTextField txtName; private javax.swing.JTextField txtName;
private javax.swing.JTextField txtPassword; private javax.swing.JTextField txtPassword;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View file

@ -204,4 +204,274 @@ public class Commander extends Constructed {
|| cardColor.isWhite() && !commander.isWhite()); || 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;
}
} }

View file

@ -298,6 +298,19 @@ public class TableController {
return false; 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); Player player = createPlayer(name, seat.getPlayerType(), skill);
if (player == null) { if (player == null) {
String message = new StringBuilder("Could not create player ").append(name).append(" of type ").append(seat.getPlayerType()).toString(); String message = new StringBuilder("Could not create player ").append(name).append(" of type ").append(seat.getPlayerType()).toString();

View file

@ -67,4 +67,8 @@ public abstract class DeckValidator implements Serializable {
} }
} }
} }
public int getEdhPowerLevel(Deck deck) {
return 0;
}
} }

View file

@ -58,6 +58,7 @@ public class MatchOptions implements Serializable {
protected SkillLevel skillLevel; protected SkillLevel skillLevel;
protected boolean rollbackTurnsAllowed; protected boolean rollbackTurnsAllowed;
protected int quitRatio; protected int quitRatio;
protected int edhPowerLevel;
protected boolean rated; protected boolean rated;
protected int numSeatsForMatch; protected int numSeatsForMatch;
@ -209,6 +210,14 @@ public class MatchOptions implements Serializable {
this.quitRatio = quitRatio; this.quitRatio = quitRatio;
} }
public int getEdhPowerLevel() {
return edhPowerLevel;
}
public void setEdhPowerLevel(int edhPowerLevel) {
this.edhPowerLevel = edhPowerLevel;
}
public boolean isRated() { public boolean isRated() {
return rated; return rated;
} }