Merge pull request #1 from magefree/master

Merge magefree/mage
This commit is contained in:
Eirkei 2016-09-24 19:26:43 +02:00 committed by GitHub
commit 738c11a0e3
64 changed files with 751 additions and 261 deletions

View file

@ -378,7 +378,7 @@ public class NewTableDialog extends MageDialog {
private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed
GameTypeView gameType = (GameTypeView) cbGameType.getSelectedItem();
MatchOptions options = new MatchOptions(this.txtName.getText(), gameType.getName());
MatchOptions options = new MatchOptions(this.txtName.getText(), gameType.getName(), false, 2);
options.getPlayerTypes().add("Human");
for (TablePlayerPanel player : players) {
options.getPlayerTypes().add(player.getPlayerType());

View file

@ -29,11 +29,8 @@
<Component id="pnlPacks" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="lblNbrPlayers" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="spnNumPlayers" min="-2" pref="46" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0"> <Component id="lblNbrPlayers" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="spnNumPlayers" min="-2" pref="46" max="-2" attributes="0"/> </Group>
<Group type="102" alignment="0" attributes="0"> <Component id="lblNbrSeats" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="spnNumSeats" min="-2" pref="46" max="-2" attributes="0"/> </Group>
<Component id="lblPacks" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblPlayer1" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
@ -175,6 +172,7 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="lblPacks" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Component id="pnlPacks" min="-2" max="-2" attributes="0"/>
@ -191,7 +189,12 @@
<Component id="spnNumPlayers" alignment="1" max="32767" attributes="1"/>
<Component id="pnlDraftOptions" alignment="1" pref="0" max="32767" attributes="1"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="lblNbrSeats" alignment="1" max="32767" attributes="0"/>
<Component id="spnNumSeats" alignment="1" max="32767" attributes="1"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblPlayer1" min="-2" pref="25" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="3" attributes="0">
@ -200,6 +203,7 @@
<Component id="chkRollbackTurnsAllowed" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="player1Panel" min="-2" pref="62" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
@ -377,17 +381,19 @@
<Property name="rows" type="int" value="0"/>
</Layout>
</Container>
<Component class="javax.swing.JLabel" name="lblNbrPlayers">
<Properties>
<Property name="text" type="java.lang.String" value="Players:"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblNbrPlayers"> <Properties> <Property name="text" type="java.lang.String" value="Players:"/> </Properties> </Component>
<Component class="javax.swing.JLabel" name="lblNbrSeats"> <Properties> <Property name="text" type="java.lang.String" value="Seats:"/> </Properties> </Component>
<Component class="javax.swing.JSpinner" name="spnNumPlayers">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="spnNumPlayersStateChanged"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="pnlDraftOptions">
<Component class="javax.swing.JSpinner" name="spnNumSeats">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="spnNumSeatsStateChanged"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="pnlDraftOptions">
<Layout>
<DimensionLayout dim="0">

View file

@ -170,6 +170,8 @@ public class NewTournamentDialog extends MageDialog {
pnlPacks = new javax.swing.JPanel();
lblNbrPlayers = new javax.swing.JLabel();
spnNumPlayers = new javax.swing.JSpinner();
lblNbrSeats = new javax.swing.JLabel();
spnNumSeats = new javax.swing.JSpinner();
pnlDraftOptions = new javax.swing.JPanel();
jLabel6 = new javax.swing.JLabel();
cbDraftTiming = new javax.swing.JComboBox();
@ -277,6 +279,14 @@ public class NewTournamentDialog extends MageDialog {
}
});
lblNbrSeats.setText("Seats:");
spnNumSeats.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
spnNumSeatsStateChanged(evt);
}
});
jLabel6.setText("Timing:");
cbDraftTiming.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
@ -358,6 +368,8 @@ public class NewTournamentDialog extends MageDialog {
lblQuitRatio.setText("Allowed quit %:");
spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table");
spnNumSeats.setToolTipText("The number of seats for each duel. If more than 2, will set number of wins to 1");
spnNumPlayers.setToolTipText("The total number of players who will draft");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
@ -373,7 +385,11 @@ public class NewTournamentDialog extends MageDialog {
.addGroup(layout.createSequentialGroup()
.addComponent(lblNbrPlayers)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblNbrSeats)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(spnNumSeats, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(lblPacks)
.addComponent(lblPlayer1))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -504,6 +520,8 @@ public class NewTournamentDialog extends MageDialog {
.addComponent(lblNumRounds))
.addComponent(lblNbrPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spnNumPlayers)
.addComponent(lblNbrSeats, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spnNumSeats)
.addComponent(pnlDraftOptions, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblPlayer1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
@ -533,7 +551,8 @@ public class NewTournamentDialog extends MageDialog {
private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOkActionPerformed
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
TournamentOptions tOptions = new TournamentOptions(this.txtName.getText());
int numSeats = (Integer)this.spnNumSeats.getValue();
TournamentOptions tOptions = new TournamentOptions(this.txtName.getText(), "", numSeats);
tOptions.setTournamentType(tournamentType.getName());
tOptions.setPassword(txtPassword.getText());
tOptions.getPlayerTypes().add("Human");
@ -653,13 +672,51 @@ public class NewTournamentDialog extends MageDialog {
this.hideDialog();
}//GEN-LAST:event_btnCancelActionPerformed
private void updateNumSeats() {
int numPlayers = (Integer)this.spnNumPlayers.getValue();
int numSeats = (Integer)this.spnNumSeats.getValue();
if (numSeats > 2) {
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
if (numSeats >= tournamentType.getMinPlayers()) {
createPlayers(numSeats - 1);
spnNumPlayers.setValue(numSeats);
} else {
numSeats = tournamentType.getMinPlayers();
createPlayers(numSeats - 1);
spnNumPlayers.setValue(numSeats);
spnNumSeats.setValue(numSeats);
}
spnNumWins.setValue(1);
}
}
private void spnNumPlayersStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumPlayersStateChanged
int numPlayers = (Integer)this.spnNumPlayers.getValue() - 1;
createPlayers(numPlayers);
int numPlayers = (Integer)this.spnNumPlayers.getValue();
createPlayers(numPlayers - 1);
int numSeats = (Integer)this.spnNumSeats.getValue();
if (numSeats > 2 && numPlayers != numSeats) {
updateNumSeats();
}
}//GEN-LAST:event_spnNumPlayersStateChanged
private void spnNumSeatsStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumSeatsStateChanged
int numSeats = (Integer)this.spnNumSeats.getValue();
if (numSeats > 2) {
this.spnNumPlayers.setEnabled(false);
} else {
this.spnNumPlayers.setEnabled(true);
}
updateNumSeats();
}//GEN-LAST:event_spnNumSeatsStateChanged
private void spnNumWinsnumPlayersChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumWinsnumPlayersChanged
// TODO add your handling code here:
int numSeats = (Integer)this.spnNumSeats.getValue();
int numWins = (Integer)this.spnNumSeats.getValue();
if (numSeats > 2) {
spnNumWins.setValue(1);
}
}//GEN-LAST:event_spnNumWinsnumPlayersChanged
private JFileChooser fcSelectDeck = null;
@ -726,6 +783,8 @@ public class NewTournamentDialog extends MageDialog {
this.spnNumPlayers.setEnabled(tournamentType.getMinPlayers() != tournamentType.getMaxPlayers());
createPlayers((Integer) spnNumPlayers.getValue() - 1);
this.spnNumSeats.setModel(new SpinnerNumberModel(2, 2, tournamentType.getMaxPlayers(), 1));
if (tournamentType.isLimited()) {
this.isRandom = tournamentType.isRandom();
this.isRichMan = tournamentType.isRichMan();
@ -914,6 +973,8 @@ public class NewTournamentDialog extends MageDialog {
}
private void drawPlayers() {
this.pnlOtherPlayers.removeAll();
for (TournamentPlayerPanel panel: players) {
@ -1119,6 +1180,7 @@ public class NewTournamentDialog extends MageDialog {
private javax.swing.JLabel lblGameType;
private javax.swing.JLabel lblName;
private javax.swing.JLabel lblNbrPlayers;
private javax.swing.JLabel lblNbrSeats;
private javax.swing.JLabel lblNumRounds;
private javax.swing.JLabel lblNumWins;
private javax.swing.JLabel lblPacks;
@ -1135,6 +1197,7 @@ public class NewTournamentDialog extends MageDialog {
private javax.swing.JSpinner spnConstructTime;
private javax.swing.JSpinner spnFreeMulligans;
private javax.swing.JSpinner spnNumPlayers;
private javax.swing.JSpinner spnNumSeats;
private javax.swing.JSpinner spnNumRounds;
private javax.swing.JSpinner spnNumWins;
private javax.swing.JSpinner spnQuitRatio;

View file

@ -1272,7 +1272,7 @@ public class TablesPanel extends javax.swing.JPanel {
return;
}
MatchOptions options = new MatchOptions("1", "Two Player Duel");
MatchOptions options = new MatchOptions("1", "Two Player Duel", false, 2);
options.getPlayerTypes().add("Human");
options.getPlayerTypes().add("Computer - mad");
options.setDeckType("Limited");

View file

@ -13,8 +13,6 @@ import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
import mage.sets.avacynrestored.GuiseOfFire;
import org.apache.log4j.Logger;
import org.mage.card.arcane.CardRenderer;
/**
@ -145,12 +143,10 @@ public class GUISizeHelper {
otherZonesCardDimension = new Dimension(CARD_IMAGE_WIDTH * otherZonesCardSize / 42, CARD_IMAGE_HEIGHT * otherZonesCardSize / 42);
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_FALLBACK, "false").equals("false")) {
otherZonesCardVerticalOffset = CardRenderer.getCardTopHeight(otherZonesCardDimension.width);
} else if (otherZonesCardSize > 29) {
otherZonesCardVerticalOffset = otherZonesCardDimension.height / 8;
} else {
if (otherZonesCardSize > 29) {
otherZonesCardVerticalOffset = otherZonesCardDimension.height / 8;
} else {
otherZonesCardVerticalOffset = otherZonesCardDimension.height / 10;
}
otherZonesCardVerticalOffset = otherZonesCardDimension.height / 10;
}
int battlefieldCardMinSize = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_CARD_BATTLEFIELD_MIN_SIZE, 10);

View file

@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
public final static int MAGE_VERSION_MAJOR = 1;
public final static int MAGE_VERSION_MINOR = 4;
public final static int MAGE_VERSION_PATCH = 15;
public final static String MAGE_VERSION_MINOR_PATCH = "v4";
public final static String MAGE_VERSION_MINOR_PATCH = "v5";
public final static String MAGE_VERSION_INFO = "";
private final int major;

View file

@ -30,7 +30,6 @@ package mage.view;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Mode;
@ -55,7 +54,6 @@ import mage.game.stack.Spell;
import mage.game.stack.StackAbility;
import mage.target.Target;
import mage.target.Targets;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
@ -81,7 +79,7 @@ public class CardView extends SimpleCardView {
protected List<String> manaCost;
protected int convertedManaCost;
protected Rarity rarity;
protected MageObjectType mageObjectType = MageObjectType.NULL;
protected boolean isAbility;
@ -323,7 +321,8 @@ public class CardView extends SimpleCardView {
this.mageObjectType = MageObjectType.SPELL;
Spell spell = (Spell) card;
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (mode.getTargets().size() > 0) {
setTargets(spellAbility.getTargets());
}
@ -331,18 +330,19 @@ public class CardView extends SimpleCardView {
}
// show for modal spell, which mode was choosen
if (spell.getSpellAbility().isModal()) {
for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) {
for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) {
Mode mode = spell.getSpellAbility().getModes().get(modeId);
this.rules.add("<span color='green'><i>Chosen mode: " + mode.getEffects().getText(mode) + "</i></span>");
}
}
}
// Frame color
this.frameColor = card.getFrameColor(game);
// Frame style
this.frameStyle = card.getFrameStyle();
// Get starting loyalty
this.startingLoyalty = "" + card.getStartingLoyalty();
}
@ -355,7 +355,7 @@ public class CardView extends SimpleCardView {
this.mageObjectType = MageObjectType.PERMANENT;
this.power = Integer.toString(object.getPower().getValue());
this.toughness = Integer.toString(object.getToughness().getValue());
this.loyalty = Integer.toString(((Permanent) object).getCounters((Game)null).getCount(CounterType.LOYALTY));
this.loyalty = Integer.toString(((Permanent) object).getCounters((Game) null).getCount(CounterType.LOYALTY));
} else {
this.power = object.getPower().toString();
this.toughness = object.getToughness().toString();
@ -488,7 +488,7 @@ public class CardView extends SimpleCardView {
this.rarity = Rarity.NA;
this.type = token.getTokenType();
this.tokenDescriptor = token.getTokenDescriptor();
this.tokenSetCode = token.getOriginalExpansionSetCode();
this.tokenSetCode = token.getOriginalExpansionSetCode();
}
protected final void setTargets(Targets targets) {
@ -547,7 +547,7 @@ public class CardView extends SimpleCardView {
public String getLoyalty() {
return loyalty;
}
public String getStartingLoyalty() {
return startingLoyalty;
}
@ -567,7 +567,7 @@ public class CardView extends SimpleCardView {
public ObjectColor getColor() {
return color;
}
public ObjectColor getFrameColor() {
return frameColor;
}
@ -807,4 +807,3 @@ public class CardView extends SimpleCardView {
}
}

View file

@ -99,7 +99,8 @@ public class StackAbilityView extends CardView {
private void updateTargets(Game game, StackAbility ability) {
List<String> names = new ArrayList<>();
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
if (mode.getTargets().size() > 0) {
setTargets(mode.getTargets());
} else {
@ -132,7 +133,8 @@ public class StackAbilityView extends CardView {
// show for modal ability, which mode was choosen
if (ability.isModal()) {
Modes modes = ability.getModes();
for (Mode mode : modes.getSelectedModes()) {
for (UUID modeId : modes.getSelectedModes()) {
Mode mode = modes.get(modeId);
this.rules.add("<span color='green'><i>Chosen mode: " + mode.getEffects().getText(mode) + "</i></span>");
}
}

View file

@ -1516,7 +1516,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
//TODO: improve this;
AvailableMode:
for (Mode mode : modes.getAvailableModes(source, game)) {
for (Mode selectedMode : modes.getSelectedModes()) {
for (UUID selectedModeId : modes.getSelectedModes()) {
Mode selectedMode = modes.get(selectedModeId);
if (selectedMode.getId().equals(mode.getId())) {
continue AvailableMode;
}

View file

@ -1326,7 +1326,8 @@ public class HumanPlayer extends PlayerImpl {
AvailableModes:
for (Mode mode : modes.getAvailableModes(source, game)) {
int timesSelected = 0;
for (Mode selectedMode : modes.getSelectedModes()) {
for (UUID selectedModeId : modes.getSelectedModes()) {
Mode selectedMode = modes.get(selectedModeId);
if (mode.getId().equals(selectedMode.getId())) {
if (modes.isEachModeMoreThanOnce()) {
timesSelected++;

View file

@ -43,6 +43,7 @@ import mage.game.events.TableEvent;
import mage.game.match.Match;
import mage.game.match.MatchOptions;
import mage.game.result.ResultProtos.TourneyQuitStatus;
import mage.game.tournament.MultiplayerRound;
import mage.game.tournament.Tournament;
import mage.game.tournament.TournamentPairing;
import mage.game.tournament.TournamentPlayer;
@ -114,6 +115,19 @@ public class TournamentController {
startMatch(event.getPair(), event.getMatchOptions());
}
break;
case START_MULTIPLAYER_MATCH:
if (!isAbort()) {
initTournament(); // set state
MatchOptions matchOptions = event.getMatchOptions();
if (matchOptions != null && event.getMultiplayerRound() != null) {
for (TournamentPlayer player : event.getMultiplayerRound().getAllPlayers()) {
matchOptions.getPlayerTypes().add(player.getPlayerType());
}
}
startMultiplayerMatch(event.getMultiplayerRound(), event.getMatchOptions());
}
break;
case END:
endTournament();
break;
@ -264,6 +278,31 @@ public class TournamentController {
logger.fatal("TournamentController startMatch error", ex);
}
}
private void startMultiplayerMatch(MultiplayerRound round, MatchOptions matchOptions) {
try {
TableManager tableManager = TableManager.getInstance();
Table table = tableManager.createTable(GamesRoomManager.getInstance().getMainRoomId(), matchOptions);
table.setTournamentSubTable(true);
table.setTournament(tournament);
table.setState(TableState.WAITING);
for (TournamentPlayer player : round.getAllPlayers()) {
tableManager.addPlayer(getPlayerUserId(player.getPlayer().getId()), table.getId(), player.getPlayer(), player.getPlayerType(), player.getDeck());
}
table.setState(TableState.STARTING);
tableManager.startTournamentSubMatch(null, table.getId());
Match match = tableManager.getMatch(table.getId());
match.setTableId(tableId);
round.setMatch(match);
round.setTableId(table.getId());
for (TournamentPlayer player : round.getAllPlayers()) {
player.setState(TournamentPlayerState.DUELING);
}
} catch (GameException ex) {
logger.fatal("TournamentController startMatch error", ex);
}
}
private void startDraft(Draft draft) {
TableManager.getInstance().startDraft(tableId, draft);

View file

@ -104,7 +104,8 @@ class ZadaHedronGrinderTriggeredAbility extends TriggeredAbilityImpl {
if (isControlledInstantOrSorcery(spell)) {
boolean targetsSource = false;
for (Ability ability : spell.getSpellAbilities()) {
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Target target : mode.getTargets()) {
if (!target.isNotTarget()) {
for (UUID targetId : target.getTargets()) {
@ -167,7 +168,8 @@ class ZadaHedronGrinderEffect extends OneShotEffect {
Target usedTarget = null;
setUsedTarget:
for (Ability ability : spell.getSpellAbilities()) {
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Target target : mode.getTargets()) {
if (!target.isNotTarget() && target.getFirstTarget().equals(source.getSourceId())) {
usedTarget = target.copy();
@ -185,7 +187,8 @@ class ZadaHedronGrinderEffect extends OneShotEffect {
Spell copy = spell.copySpell(source.getControllerId());
game.getStack().push(copy);
setTarget:
for (Mode mode : copy.getSpellAbility().getModes().getSelectedModes()) {
for (UUID modeId : copy.getSpellAbility().getModes().getSelectedModes()) {
Mode mode = copy.getSpellAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
if (target.getClass().equals(usedTarget.getClass())) {
target.clearChosen(); // For targets with Max > 1 we need to clear before the text is comapred

View file

@ -70,7 +70,7 @@ public class GoblinArtisans extends CardImpl {
// {tap}: Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GoblinArtisansEffect(), new TapSourceCost()));
}
public GoblinArtisans(final GoblinArtisans card) {
@ -83,15 +83,14 @@ public class GoblinArtisans extends CardImpl {
}
}
class GoblinArtisansEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("permanent named Goblin Artisans");
static {
filter.add(new NamePredicate("Goblin Artisans"));
}
public GoblinArtisansEffect() {
super(Outcome.Damage);
staticText = "Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans.";
@ -109,37 +108,38 @@ class GoblinArtisansEffect extends OneShotEffect {
controller.drawCards(1, game);
} else {
List<Permanent> artifacts = game.getBattlefield().getActivePermanents(new FilterControlledArtifactPermanent(), source.getControllerId(), game);
if (artifacts.isEmpty()){//Don't even bother if there is no artifact to 'counter'/sacrifice
if (artifacts.isEmpty()) {//Don't even bother if there is no artifact to 'counter'/sacrifice
return true;
}
}
filter.add(Predicates.not(new PermanentIdPredicate(source.getSourceId())));
//removed the activating instance of Artisans, btw, wasn't that filter declared as static final? How come I can do this here? :)
List<Permanent> list = game.getBattlefield().getAllActivePermanents(filter, game);
for (Permanent perm : list){ // should I limit below for a particular kind of ability? Going for the most general, it's unlikely there'll be any other artisans anyway, so not concerned about efficiency :p
for (Ability abil : perm.getAbilities(game)){//below is copied from TargetsPermanentPredicate, but why only "selectedModes"? Shouldnt be more general as well?
for (Mode mode : abil.getModes().getSelectedModes()){
//removed the activating instance of Artisans, btw, wasn't that filter declared as static final? How come I can do this here? :)
List<Permanent> list = game.getBattlefield().getAllActivePermanents(filter, game);
for (Permanent perm : list) { // should I limit below for a particular kind of ability? Going for the most general, it's unlikely there'll be any other artisans anyway, so not concerned about efficiency :p
for (Ability abil : perm.getAbilities(game)) {//below is copied from TargetsPermanentPredicate, but why only "selectedModes"? Shouldnt be more general as well?
for (UUID modeId : abil.getModes().getSelectedModes()) {
Mode mode = abil.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
artifacts.remove(game.getPermanentOrLKIBattlefield(targetId));
}// we could
}// remove this
}//closing bracers
}// pyramid, if it's bothering anyone
} //they are all one-liners after all :)
if (!artifacts.isEmpty()){
Cards cards=new CardsImpl();
for (Permanent perm : artifacts){
for (UUID targetId : target.getTargets()) {
artifacts.remove(game.getPermanentOrLKIBattlefield(targetId));
}// we could
}// remove this
}//closing bracers
}// pyramid, if it's bothering anyone
} //they are all one-liners after all :)
if (!artifacts.isEmpty()) {
Cards cards = new CardsImpl();
for (Permanent perm : artifacts) {
cards.add(perm.getId());
}
TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard());
TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard());
controller.choose(Outcome.Sacrifice, cards, target, game);
game.getPermanent(target.getFirstTarget()).sacrifice(source.getSourceId(), game);
}
game.getPermanent(target.getFirstTarget()).sacrifice(source.getSourceId(), game);
}
return true;
}
}
}
return false;
}

View file

@ -120,7 +120,7 @@ class JelevaNephaliasScourgeEffect extends OneShotEffect {
for (int i = 0; i < cardsToExile; i++) {
Card card = player.getLibrary().removeFromTop(game);
if (card != null) {
card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceCard.getName(), source.getSourceId(), game);
card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceCard.getIdName(), source.getSourceId(), game);
}
}
}

View file

@ -227,7 +227,8 @@ class IcefallRegentCostIncreaseEffect extends CostModificationEffectImpl {
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {

View file

@ -109,7 +109,8 @@ class MonasterySiegeCostIncreaseEffect extends CostModificationEffectImpl {
if (new ModeChoiceSourceCondition("Dragons").apply(game, source)) {
if (abilityToModify instanceof SpellAbility) {
if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getControllerId())) {

View file

@ -45,7 +45,6 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.players.PlayerList;
/**
*
@ -103,31 +102,30 @@ class ZursWeirdingReplacementEffect extends ReplacementEffectImpl {
Card card = player.getLibrary().getFromTop(game);
if (card != null) {
// reveals it instead
player.revealCards(sourceObject.getIdName() + " next draw of " + player.getName() + " (" + game.getTurnNum()+"|"+game.getPhase().getType() +")", new CardsImpl(card), game);
player.revealCards(sourceObject.getIdName() + " next draw of " + player.getName() + " (" + game.getTurnNum() + "|" + game.getPhase().getType() + ")", new CardsImpl(card), game);
// Then any other player may pay 2 life. If a player does, put that card into its owner's graveyard
PlayerList playerList = game.getPlayerList().copy();
playerList.setCurrent(player.getId());
Player currentPlayer = playerList.getNext(game);
String message = new StringBuilder("Pay 2 life to put ").append(card.getLogName()).append(" into graveyard?").toString();
while (!currentPlayer.getId().equals(player.getId())) {
if (currentPlayer.canPayLifeCost() &&
currentPlayer.getLife() >= 2 &&
currentPlayer.chooseUse(Outcome.Benefit, message, source, game)) {
currentPlayer.loseLife(2, game);
player.moveCards(card, Zone.GRAVEYARD, source, game);
// game.getState().getRevealed().reset();
return true;
String message = "Pay 2 life to put " + card.getLogName() + " into graveyard?";
for (UUID playerId : game.getState().getPlayersInRange(player.getId(), game)) {
if (playerId.equals(player.getId())) {
continue;
}
Player otherPlayer = game.getPlayer(playerId);
if (otherPlayer.canPayLifeCost()
&& otherPlayer.getLife() >= 2
&& otherPlayer.chooseUse(Outcome.Benefit, message, source, game)) {
otherPlayer.loseLife(2, game);
player.moveCards(card, Zone.GRAVEYARD, source, game);
break;
}
currentPlayer = playerList.getNext(game);
}
// game.getState().getRevealed().reset();
}
return true;
}
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_CARD;

View file

@ -141,7 +141,8 @@ class CapturedByTheConsulateTriggeredAbility extends TriggeredAbilityImpl {
}
if (stackObject != null) {
int numberOfTargets = 0;
for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
Mode mode = stackObject.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
numberOfTargets += target.getTargets().size();
}

View file

@ -93,7 +93,7 @@ class DeadlockTrapCantActivateEffect extends RestrictionEffect {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return getTargetPointer().getFirst(game, source).equals(permanent.getId());
return permanent.getId().equals(getTargetPointer().getFirst(game, source));
}
@Override

View file

@ -135,7 +135,8 @@ class PsychicRebuttalPredicate implements ObjectPlayerPredicate<ObjectPlayer<Sta
if (controllerId == null) {
return false;
}
for (Mode mode : input.getObject().getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : input.getObject().getStackAbility().getModes().getSelectedModes()) {
Mode mode = input.getObject().getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
if (controllerId.equals(targetId)) {

View file

@ -106,7 +106,8 @@ class ElderwoodScionCostReductionEffect extends CostModificationEffectImpl {
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility || abilityToModify instanceof FlashbackAbility) {
if (abilityToModify.getControllerId().equals(source.getControllerId())) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
if (targetUUID.equals(source.getSourceId())) {

View file

@ -28,7 +28,6 @@
package mage.sets.scourge;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
@ -108,7 +107,8 @@ class GripOfChaosTriggeredAbility extends TriggeredAbilityImpl {
}
if (stackObject != null) {
int numberOfTargets = 0;
for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
Mode mode = stackObject.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
numberOfTargets += target.getTargets().size();
}
@ -149,7 +149,8 @@ class GripOfChaosEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
StackObject stackObject = game.getStack().getStackObject(this.getTargetPointer().getFirst(game, source));
if (stackObject != null) {
for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
Mode mode = stackObject.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
UUID oldTargetId = target.getFirstTarget();
Set<UUID> possibleTargets = target.possibleTargets(source.getSourceId(), source.getControllerId(), game);

View file

@ -140,7 +140,8 @@ class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
Permanent permanent = game.getPermanent(targetUUID);

View file

@ -100,8 +100,10 @@ class ForkInTheRoadEffect extends OneShotEffect {
if (target.getTargets().size() > 0) {
Cards revealed = new CardsImpl();
for (UUID cardId : target.getTargets()) {
Card card = controller.getLibrary().getCard(cardId, game);
revealed.add(card);
Card card = game.getCard(cardId);
if (card != null) {
revealed.add(card);
}
}
controller.revealCards(sourceObject.getIdName(), revealed, game);
if (target.getTargets().size() > 0) {

View file

@ -155,7 +155,8 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl {
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility) {
if (source.getControllerId().equals(abilityToModify.getControllerId())) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetUUID : target.getTargets()) {
Permanent enchantment = game.getPermanent(source.getSourceId());

View file

@ -84,7 +84,8 @@ class HinderingLightPredicate implements ObjectPlayerPredicate<ObjectPlayer<Stac
if (controllerId == null) {
return false;
}
for (Mode mode : input.getObject().getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : input.getObject().getStackAbility().getModes().getSelectedModes()) {
Mode mode = input.getObject().getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
if (controllerId.equals(targetId)) {

View file

@ -113,13 +113,13 @@ class TorchlingTargetPredicate implements Predicate<MageObject> {
if (spell != null) {
int numberOfTargets = 0;
for (SpellAbility spellAbility : spell.getSpellAbilities()) {
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
if (!targetId.equals(sourceId)) {
return false;
}
else {
} else {
numberOfTargets++;
}
}

View file

@ -96,7 +96,8 @@ class KaerveksTorchCostIncreaseEffect extends CostModificationEffectImpl {
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify instanceof SpellAbility || abilityToModify instanceof FlashbackAbility) {
for (Mode mode : abilityToModify.getModes().getSelectedModes()) {
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
Mode mode = abilityToModify.getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
if (targetId.equals(source.getSourceObject(game).getId())) {

View file

@ -28,12 +28,6 @@
package mage.sets.worldwake;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Duration;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.SourceHasCounterCondition;
@ -41,6 +35,11 @@ import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
@ -54,7 +53,7 @@ import mage.filter.predicate.permanent.ControllerPredicate;
*/
public class QuestForTheGoblinLord extends CardImpl {
private static final String rule = "As long as Quest for the Goblin Lord has five or more quest counters on it, creatures you control get +2/+0.";
private static final String rule = "As long as {this} has five or more quest counters on it, creatures you control get +2/+0";
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
private static final FilterPermanent goblinFilter = new FilterControlledCreaturePermanent();
@ -67,7 +66,6 @@ public class QuestForTheGoblinLord extends CardImpl {
super(ownerId, 86, "Quest for the Goblin Lord", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{R}");
this.expansionSetCode = "WWK";
// Whenever a Goblin enters the battlefield under your control, you may put a quest counter on Quest for the Goblin Lord.
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.QUEST.createInstance()), goblinFilter, true));

View file

@ -38,10 +38,6 @@ import org.mage.test.serverside.base.CardTestPlayerBaseAI;
*/
public class CastDestroySpellsTest extends CardTestPlayerBaseAI {
/**
*
*
*/
@Test
public void testOrzhovCharm() {
// Choose one -
@ -58,6 +54,8 @@ public class CastDestroySpellsTest extends CardTestPlayerBaseAI {
// Cycling abilities you activate cost you up to {2} less to activate.
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Orzhov Charm", "Silvercoat Lion");
setModeChoice(playerA, "2");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();

View file

@ -367,6 +367,31 @@ public class FlashbackTest extends CardTestPlayerBase {
}
@Test
public void testSnapcasterMageWithBuyback() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 8);
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
// Buyback {5}(You may pay an additional {5} as you cast this spell. If you do, put this card into your hand as it resolves.)
// Draw a card.
addCard(Zone.GRAVEYARD, playerA, "Whispers of the Muse", 1); // {U}
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
setChoice(playerA, "Whispers of the Muse");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback"); // Flashback Whispers of the Muse
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Snapcaster Mage", 1);
assertGraveyardCount(playerA, "Whispers of the Muse", 0);
assertHandCount(playerA, 1);
assertExileCount("Whispers of the Muse", 1);
}
/**
* Deep Analysis doesn't cost mana when flashbacked.
*/

View file

@ -219,7 +219,7 @@ public class UndyingTest extends CardTestPlayerBase {
}
/**
* Tatterkite is getting counters on it, i have him in a edh deck with
* Tatterkite is getting counters on it, I have him in a edh deck with
* Mikaeus, the Lunarch and when Tatterkite dies it triggers the undying and
* he gets the +1/+1 counters
*/

View file

@ -0,0 +1,107 @@
/*
* 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 org.mage.test.cards.modal;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class OneOrBothTest extends CardTestPlayerBase {
@Test
public void testSubtleStrikeFirstMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "1");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
}
@Test
public void testSubtleStrikeSecondMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "2");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 3, 5);
}
@Test
public void testSubtleStrikeBothModes() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
addTarget(playerA, "Silvercoat Lion");
setModeChoice(playerA, "1");
setModeChoice(playerA, "2");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
}
}

View file

@ -39,20 +39,22 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class BrutalExpulsionTest extends CardTestPlayerBase {
/**
* Brutal Expulsion targeting Gideon, Ally of Zendikar. Gideon has 3 loyalty. Brutal Expulsion resolves,
* leaves 1 loyalty. I attack Gideon for 1 with a Scion token, Gideon dies. Instead of going to graveyard,
* Expulsion sends Gideon to exile. However, in game Gideon went to graveyard.
* Brutal Expulsion targeting Gideon, Ally of Zendikar. Gideon has 3
* loyalty. Brutal Expulsion resolves, leaves 1 loyalty. I attack Gideon for
* 1 with a Scion token, Gideon dies. Instead of going to graveyard,
* Expulsion sends Gideon to exile. However, in game Gideon went to
* graveyard.
*/
@Test
public void testPlaneswalkerExile() {
// Choose one or both
// - Return target spell or creature to its owner's hand;
// or Brutal Expulsion deals 2 damage to target creature or planeswalker. If that permanent would be put into a graveyard this turn, exile it instead.
addCard(Zone.HAND, playerA, "Brutal Expulsion");
addCard(Zone.HAND, playerA, "Brutal Expulsion"); // {2}{U}{R}
// Shock deals 2 damage to target creature or player.
addCard(Zone.HAND, playerA, "Shock");
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Shock"); // {R}
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
// Planeswalker with 4 loyalty.
addCard(Zone.BATTLEFIELD, playerB, "Gideon, Ally of Zendikar");
@ -60,12 +62,12 @@ public class BrutalExpulsionTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brutal Expulsion", playerB);
setModeChoice(playerA, "2");
setModeChoice(playerA, null);
setChoice(playerA, "Yes");
setChoice(playerA, "Yes"); // Redirect to planeswalker
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB);
setChoice(playerA, "Yes");
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Shock", playerB);
setChoice(playerA, "Yes"); // Redirect to planeswalker
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
setStopAt(1, PhaseStep.END_COMBAT);
execute();
assertPermanentCount(playerB, "Gideon, Ally of Zendikar", 0);

View file

@ -267,7 +267,7 @@ public class LoadTest {
* @return
*/
private MatchOptions createGameOptions(GameTypeView gameTypeView, Session session) {
MatchOptions options = new MatchOptions("Test game", gameTypeView.getName());
MatchOptions options = new MatchOptions("Test game", gameTypeView.getName(), false, 2);
options.getPlayerTypes().add("Human");
options.getPlayerTypes().add("Human");

View file

@ -287,7 +287,8 @@ public class TestPlayer implements Player {
}
UUID modeId = ability.getModes().getModeId(modeNr);
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID currentModeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(currentModeId);
if (mode.getId().equals(modeId)) {
selectedMode = mode;
ability.getModes().setActiveMode(mode);

View file

@ -306,7 +306,8 @@ public abstract class AbilityImpl implements Ability {
&& game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) {
return false;
}
for (Mode mode : this.getModes().getSelectedModes()) {
for (UUID modeId : this.getModes().getSelectedModes()) {
Mode mode = this.getModes().get(modeId);
this.getModes().setActiveMode(mode);
//20121001 - 601.2c
// 601.2c The player announces his or her choice of an appropriate player, object, or zone for
@ -1060,7 +1061,8 @@ public abstract class AbilityImpl implements Ability {
}
} else if (object instanceof Spell && ((Spell) object).getSpellAbility().getModes().size() > 1) {
Modes spellModes = ((Spell) object).getSpellAbility().getModes();
for (Mode selectedMode : spellModes.getSelectedModes()) {
for (UUID selectedModeId : spellModes.getSelectedModes()) {
Mode selectedMode = spellModes.get(selectedModeId);
int item = 0;
for (Mode mode : spellModes.values()) {
item++;

View file

@ -41,7 +41,6 @@ import mage.constants.TargetController;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
/**
*
@ -49,8 +48,8 @@ import mage.util.CardUtil;
*/
public class Modes extends LinkedHashMap<UUID, Mode> {
private Mode mode; // the current mode of the selected modes
private final ArrayList<Mode> selectedModes = new ArrayList<>();
private Mode currentMode; // the current mode of the selected modes
private final ArrayList<UUID> selectedModes = new ArrayList<>();
private int minModes;
private int maxModes;
private TargetController modeChooser;
@ -58,11 +57,11 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
public Modes() {
this.mode = new Mode();
this.put(mode.getId(), mode);
this.currentMode = new Mode();
this.put(currentMode.getId(), currentMode);
this.minModes = 1;
this.maxModes = 1;
this.selectedModes.add(mode);
this.selectedModes.add(currentMode.getId());
this.modeChooser = TargetController.YOU;
this.eachModeOnlyOnce = false;
this.eachModeMoreThanOnce = false;
@ -75,23 +74,8 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
this.minModes = modes.minModes;
this.maxModes = modes.maxModes;
if (modes.size() == 1) {
this.mode = values().iterator().next();
this.selectedModes.add(mode);
} else {
// probably there is still a problem with copying modes with the same mode selected multiple times.
for (Mode selectedMode : modes.getSelectedModes()) {
Mode copiedMode = selectedMode.copy();
this.selectedModes.add(copiedMode);
if (modes.getSelectedModes().size() == 1) {
this.mode = copiedMode;
} else {
if (selectedMode.equals(modes.getMode())) {
this.mode = copiedMode;
}
}
}
}
this.currentMode = values().iterator().next();
selectedModes.addAll(modes.getSelectedModes());
this.modeChooser = modes.modeChooser;
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
@ -102,21 +86,21 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
public Mode getMode() {
return mode;
return currentMode;
}
public UUID getModeId(int index) {
int idx = 0;
for (Mode currentMode : this.values()) {
for (Mode mode : this.values()) {
idx++;
if (idx == index) {
return currentMode.getId();
return mode.getId();
}
}
return null;
}
public ArrayList<Mode> getSelectedModes() {
public ArrayList<UUID> getSelectedModes() {
return selectedModes;
}
@ -145,8 +129,8 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
public void setActiveMode(Mode mode) {
if (selectedModes.contains(mode)) {
this.mode = mode;
if (selectedModes.contains(mode.getId())) {
this.currentMode = mode;
}
}
@ -175,7 +159,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
for (Mode mode : this.values()) {
if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId()))
&& mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) {
this.selectedModes.add(mode.copy());
this.selectedModes.add(mode.getId());
}
}
if (isEachModeOnlyOnce()) {
@ -203,7 +187,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
Player player = game.getPlayer(playerId);
// player chooses modes manually
this.mode = null;
this.currentMode = null;
while (this.selectedModes.size() < this.getMaxModes()) {
Mode choice = player.chooseMode(this, source, game);
if (choice == null) {
@ -212,9 +196,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
return this.selectedModes.size() >= this.getMinModes();
}
this.selectedModes.add(choice.copy());
if (mode == null) {
mode = choice;
this.selectedModes.add(choice.getId());
if (currentMode == null) {
currentMode = choice;
}
}
if (isEachModeOnlyOnce()) {
@ -222,10 +206,10 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
return true;
}
if (mode == null) {
if (currentMode == null) {
this.selectedModes.clear();
Mode copiedMode = this.values().iterator().next().copy();
this.selectedModes.add(copiedMode);
this.selectedModes.add(copiedMode.getId());
this.setActiveMode(copiedMode);
}
if (isEachModeOnlyOnce()) {
@ -234,27 +218,46 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
return true;
}
private void setAlreadySelectedModes(ArrayList<Mode> selectedModes, Ability source, Game game) {
String key = getKey(source, game);
Set<UUID> onceSelectedModes = (Set<UUID>) game.getState().getValue(key);
if (onceSelectedModes == null) {
onceSelectedModes = new HashSet<>();
/**
* Saves the already selected modes to the state value
*
* @param selectedModes
* @param source
* @param game
*/
private void setAlreadySelectedModes(ArrayList<UUID> selectedModes, Ability source, Game game) {
for (UUID modeId : selectedModes) {
String key = getKey(source, game, modeId);
game.getState().setValue(key, true);
}
for (Mode mode : selectedModes) {
onceSelectedModes.add(mode.getId());
}
game.getState().setValue(key, onceSelectedModes);
}
// The already once selected modes for a modal card are stored as a state value
// That's important for modal abilities with modes that can only selected once while the object stays in its zone
@SuppressWarnings("unchecked")
private Set<UUID> getAlreadySelectedModes(Ability source, Game game) {
return (Set<UUID>) game.getState().getValue(getKey(source, game));
Set<UUID> onceSelectedModes = new HashSet<>();
for (UUID modeId : this.keySet()) {
Object exist = game.getState().getValue(getKey(source, game, modeId));
if (exist != null) {
onceSelectedModes.add(modeId);
}
}
return onceSelectedModes;
}
private String getKey(Ability source, Game game) {
return CardUtil.getObjectZoneString("selectedModes", source.getSourceId(), game, game.getState().getZoneChangeCounter(source.getSourceId()), false);
// creates the key the selected modes are saved with to the state values
private String getKey(Ability source, Game game, UUID modeId) {
return source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + modeId.toString();
}
/**
* Returns all (still) available modes of the ability
*
* @param source
* @param game
* @return
*/
public List<Mode> getAvailableModes(Ability source, Game game) {
List<Mode> availableModes = new ArrayList<>();
Set<UUID> nonAvailableModes;

View file

@ -56,6 +56,9 @@ public abstract class StaticAbility extends AbilityImpl {
if (game.getShortLivingLKI(getSourceId(), zone)) {
return true; // maybe this can be a problem if effects removed the ability from the object
}
if (game.getPermanentEntering(getSourceId()) != null && zone.equals(Zone.BATTLEFIELD)) {
return true; // abilities of permanents entering battlefield are countes as on battlefield
}
return super.isInUseableZone(game, source, event);
}

View file

@ -88,7 +88,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
MageObject object = game.getObject(getSourceId());
Player player = game.getPlayer(this.getControllerId());
if (player != null && object != null) {
if (!player.chooseUse(getEffects().get(0).getOutcome(), (object != null ? this.getRule(object.getLogName()) : this.getRule()), this, game)) {
if (!player.chooseUse(getEffects().get(0).getOutcome(), this.getRule(object.getLogName()), this, game)) {
return false;
}
} else {
@ -216,7 +216,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
/*
603.6c,603.6d
This has to be set, if the triggered ability has to check back in time if the permanent the ability is connected to had the ability on the battlefeild while the trigger is checked
This has to be set, if the triggered ability has to check back in time if the permanent the ability is connected to had the ability on the battlefield while the trigger is checked
*/
@Override
public final void setLeavesTheBattlefieldTrigger(boolean leavesTheBattlefieldTrigger) {

View file

@ -51,7 +51,8 @@ public class ChangeATargetOfTargetSpellAbilityToSourceEffect extends OneShotEffe
} else {
return false;
}
for (Mode mode : sourceAbility.getModes().getSelectedModes()) {
for (UUID modeId : sourceAbility.getModes().getSelectedModes()) {
Mode mode = sourceAbility.getModes().get(modeId);
targets.addAll(mode.getTargets());
}
@ -102,12 +103,10 @@ public class ChangeATargetOfTargetSpellAbilityToSourceEffect extends OneShotEffe
}
if (oldTargetName != null) {
game.informPlayers(sourceObject.getLogName() + ": Changed target of " + stackObject.getLogName() + " from " + oldTargetName + " to " + sourceObject.getLogName());
} else if (twoTimesTarget) {
game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its not valid to target it twice for " + stackObject.getLogName());
} else {
if (twoTimesTarget) {
game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its not valid to target it twice for " + stackObject.getLogName());
} else {
game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its no valid target for " + stackObject.getLogName());
}
game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its no valid target for " + stackObject.getLogName());
}
return true;
}

View file

@ -186,7 +186,7 @@ class BuybackEffect extends ReplacementEffectImpl {
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(source.getSourceId())) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getFromZone() == Zone.STACK
if (zEvent.getFromZone() == Zone.STACK && zEvent.getToZone() == Zone.GRAVEYARD
&& source.getSourceId().equals(event.getSourceId())) { // if spell fizzled, the sourceId is null
return true;
}

View file

@ -274,7 +274,8 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
if (controller != null) {
Card card = game.getCard(event.getTargetId());
if (card != null) {
return controller.moveCards(card, Zone.EXILED, source, game);
return controller.moveCards(
card, Zone.EXILED, source, game, false, false, false, event.getAppliedEffects());
}
}
return false;

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
@ -82,7 +82,8 @@ public class HeroicAbility extends TriggeredAbilityImpl {
private boolean checkSpell(Spell spell, Game game) {
if (spell != null) {
SpellAbility sa = spell.getSpellAbility();
for (Mode mode : sa.getModes().getSelectedModes()) {
for (UUID modeId : sa.getModes().getSelectedModes()) {
Mode mode = sa.getModes().get(modeId);
for (Target target : mode.getTargets()) {
if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) {
return true;

View file

@ -63,7 +63,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 47;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 61;
private static final long CARD_CONTENT_VERSION = 62;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;

View file

@ -27,6 +27,7 @@
*/
package mage.filter.predicate.mageobject;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Mode;
import mage.filter.predicate.Predicate;
@ -51,7 +52,8 @@ public class NumberOfTargetsPredicate implements Predicate<MageObject> {
StackObject stackObject = game.getState().getStack().getStackObject(input.getId());
if (stackObject != null) {
int numberOfTargets = 0;
for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
Mode mode = stackObject.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
numberOfTargets += target.getTargets().size();
}

View file

@ -54,7 +54,8 @@ public class TargetsPermanentPredicate implements ObjectSourcePlayerPredicate<Ob
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
StackObject object = game.getStack().getStackObject(input.getObject().getId());
if (object != null) {
for (Mode mode : object.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) {
Mode mode = object.getStackAbility().getModes().get(modeId);
for (Target target : mode.getTargets()) {
for (UUID targetId : target.getTargets()) {
Permanent permanent = game.getPermanentOrLKIBattlefield(targetId);

View file

@ -314,7 +314,8 @@ public class GameState implements Serializable, Copyable<GameState> {
for (StackObject spell : stack) {
sb.append(spell.getControllerId()).append(spell.getName());
sb.append(spell.getStackAbility().toString());
for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : spell.getStackAbility().getModes().getSelectedModes()) {
Mode mode = spell.getStackAbility().getModes().get(modeId);
if (!mode.getTargets().isEmpty()) {
sb.append("targets");
for (Target target : mode.getTargets()) {
@ -366,7 +367,8 @@ public class GameState implements Serializable, Copyable<GameState> {
for (StackObject spell : stack) {
sb.append(spell.getControllerId()).append(spell.getName());
sb.append(spell.getStackAbility().toString());
for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) {
for (UUID modeId : spell.getStackAbility().getModes().getSelectedModes()) {
Mode mode = spell.getStackAbility().getModes().get(modeId);
if (!mode.getTargets().isEmpty()) {
sb.append("targets");
for (Target target : mode.getTargets()) {
@ -784,7 +786,8 @@ public class GameState implements Serializable, Copyable<GameState> {
public void addAbility(Ability ability, MageObject attachedTo) {
if (ability instanceof StaticAbility) {
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Effect effect : mode.getEffects()) {
if (effect instanceof ContinuousEffect) {
addEffect((ContinuousEffect) effect, ability);
@ -806,7 +809,8 @@ public class GameState implements Serializable, Copyable<GameState> {
*/
public void addAbility(Ability ability, UUID sourceId, Card attachedTo) {
if (ability instanceof StaticAbility) {
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Effect effect : mode.getEffects()) {
if (effect instanceof ContinuousEffect) {
addEffect((ContinuousEffect) effect, sourceId, ability);

View file

@ -237,6 +237,9 @@ public class Combat implements Serializable, Copyable<Combat> {
possibleDefenders = new HashSet<>(defenders);
}
Player player = game.getPlayer(attackerId);
if (player == null) {
return false;
}
if (possibleDefenders.size() == 1) {
addAttackerToCombat(creatureId, possibleDefenders.iterator().next(), game);
return true;
@ -866,7 +869,8 @@ public class Combat implements Serializable, Copyable<Combat> {
blockIsValid = true;
break CombatGroups;
} else // check if the blocker blocks a attacker that must be blocked at least by one and is the only blocker, this block is also valid
if (combatGroup.getBlockers().size() == 1) {
{
if (combatGroup.getBlockers().size() == 1) {
if (mustBeBlockedByAtLeastOne.containsKey(forcingAttackerId)) {
if (mustBeBlockedByAtLeastOne.get(forcingAttackerId).contains(creatureForcedToBlock.getId())) {
blockIsValid = true;
@ -874,6 +878,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
}
}
}
}
}
@ -921,8 +926,8 @@ public class Combat implements Serializable, Copyable<Combat> {
if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) {
// blocks a creature that has to be blocked by at least one
if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) {
Set <UUID> blockedSet = mustBeBlockedByAtLeastOne.get(blockedAttackerId);
Set <UUID> toBlockSet = mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId);
Set<UUID> blockedSet = mustBeBlockedByAtLeastOne.get(blockedAttackerId);
Set<UUID> toBlockSet = mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId);
if (toBlockSet == null) {
// This should never happen.
return null;

View file

@ -36,6 +36,7 @@ import mage.cards.decks.Deck;
import mage.game.Game;
import mage.game.draft.Draft;
import mage.game.match.MatchOptions;
import mage.game.tournament.MultiplayerRound;
import mage.game.tournament.TournamentPairing;
/**
@ -46,7 +47,7 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab
public enum EventType {
UPDATE, INFO, STATUS, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, END_GAME_INFO, ERROR,
INIT_TIMER, RESUME_TIMER, PAUSE_TIMER, CHECK_STATE_PLAYERS
INIT_TIMER, RESUME_TIMER, PAUSE_TIMER, CHECK_STATE_PLAYERS, START_MULTIPLAYER_MATCH
}
private Game game;
@ -58,6 +59,7 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab
private UUID playerId;
private Deck deck;
private TournamentPairing pair;
private MultiplayerRound round;
private MatchOptions options;
private int timeout;
private boolean withTime;
@ -115,6 +117,13 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab
this.options = options;
this.eventType = eventType;
}
public TableEvent(EventType eventType, MultiplayerRound round, MatchOptions options) {
super(options);
this.round = round;
this.options = options;
this.eventType = eventType;
}
public Game getGame() {
return game;
@ -151,6 +160,10 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab
public TournamentPairing getPair() {
return pair;
}
public MultiplayerRound getMultiplayerRound() {
return round;
}
public MatchOptions getMatchOptions() {
return options;

View file

@ -38,6 +38,7 @@ import mage.game.tournament.TournamentPairing;
import java.io.Serializable;
import java.util.UUID;
import mage.game.tournament.MultiplayerRound;
/**
*
@ -93,4 +94,8 @@ public class TableEventSource implements EventSource<TableEvent>, Serializable {
public void fireTableEvent(EventType eventType, TournamentPairing pair, MatchOptions options) {
dispatcher.fireEvent(new TableEvent(eventType, pair, options));
}
public void fireTableEvent(EventType eventType, MultiplayerRound round, MatchOptions options) {
dispatcher.fireEvent(new TableEvent(eventType, round, options));
}
}

View file

@ -52,21 +52,50 @@ public class MatchOptions implements Serializable {
protected String deckType;
protected boolean limited;
protected List<String> playerTypes = new ArrayList<>();
protected boolean multiPlayer;
protected int numSeats;
protected String password;
protected SkillLevel skillLevel;
protected boolean rollbackTurnsAllowed;
protected int quitRatio;
protected boolean rated;
protected int numSeatsForMatch;
/**
* Time each player has during the game to play using his\her priority.
*/
protected MatchTimeLimit matchTimeLimit; // 0 = no priorityTime handling
public MatchOptions(String name, String gameType) {
/*public MatchOptions(String name, String gameType) {
this.name = name;
this.gameType = gameType;
this.password = "";
this.multiPlayer = false;
this.numSeats = 2;
}*/
public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats ) {
this.name = name;
this.gameType = gameType;
this.password = "";
this.multiPlayer = multiPlayer;
this.numSeats = numSeats;
}
public void setNumSeats (int numSeats) {
this.numSeats = numSeats;
}
public int getNumSeats () {
return numSeats;
}
public void setMultiPlayer(boolean multiPlayer) {
this.multiPlayer = multiPlayer;
}
public boolean getMultiPlayer() {
return multiPlayer;
}
public String getName() {

View file

@ -210,7 +210,8 @@ public class Spell extends StackObjImpl implements Card {
if (notTargeted || legalParts) {
for (SpellAbility spellAbility : this.spellAbilities) {
if (spellAbilityHasLegalParts(spellAbility, game)) {
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
spellAbility.getModes().setActiveMode(mode);
if (mode.getTargets().stillLegal(spellAbility, game)) {
if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
@ -283,7 +284,8 @@ public class Spell extends StackObjImpl implements Card {
private boolean hasTargets(SpellAbility spellAbility, Game game) {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (!mode.getTargets().isEmpty()) {
return true;
}
@ -299,7 +301,8 @@ public class Spell extends StackObjImpl implements Card {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
boolean targetedMode = false;
boolean legalTargetedMode = false;
for (Mode mode : spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (mode.getTargets().size() > 0) {
targetedMode = true;
if (mode.getTargets().stillLegal(spellAbility, game)) {

View file

@ -117,7 +117,8 @@ public abstract class StackObjImpl implements StackObject {
}
for (Ability ability : objectAbilities) {
// Some spells can have more than one mode
for (Mode mode : ability.getModes().getSelectedModes()) {
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
ability.getModes().setActiveMode(mode);
oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game));
for (Target target : mode.getTargets()) {
@ -210,8 +211,7 @@ public abstract class StackObjImpl implements StackObject {
again = true;
}
} else // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition
{
if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) {
if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) {
if (targetController.isHuman()) {
if (targetController.chooseUse(Outcome.Benefit, "This target was already selected from origin spell. Reset to original target?", ability, game)) {
// use previous target no target was selected
@ -240,7 +240,6 @@ public abstract class StackObjImpl implements StackObject {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false);
}
}
} while (again && targetController.canRespond());
}
} // keep the target

View file

@ -0,0 +1,97 @@
/*
* Copyright 2011 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.game.tournament;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.game.match.Match;
/**
*
* @author spjspj
*/
public class MultiplayerRound {
private final int roundNum;
private final Tournament tournament;
private final int numSeats;
private final List<TournamentPlayer> allPlayers = new ArrayList<>();
private Match match;
private UUID tableId;
public MultiplayerRound(int roundNum, Tournament tournament, int numSeats) {
this.roundNum = roundNum;
this.tournament = tournament;
this.numSeats = numSeats;
}
public List<TournamentPlayer> getAllPlayers () {
return allPlayers;
}
public TournamentPlayer getPlayer (int i) {
if (i >= 0 && i < numSeats && i < allPlayers.size()) {
return allPlayers.get(i);
}
return null;
}
public void addPairing(TournamentPairing match) {
this.allPlayers.add(match.getPlayer1());
this.allPlayers.add(match.getPlayer2());
}
public void addPlayer(TournamentPlayer player) {
this.allPlayers.add(player);
}
public int getRoundNumber() {
return this.roundNum;
}
public void setMatch (Match match) {
this.match = match;
}
public void setTableId (UUID tableId) {
this.tableId = tableId;
}
public boolean isRoundOver() {
boolean roundIsOver = true;
if (this.match != null) {
if (!this.match.hasEnded()) {
roundIsOver = false;
}
}
return roundIsOver;
}
}

View file

@ -261,6 +261,12 @@ public abstract class TournamentImpl implements Tournament {
}
updateResults();
}
protected void playMultiplayerRound(MultiplayerRound round) {
playMultiPlayerMatch(round);
updateResults(); // show points from byes
}
protected List<TournamentPlayer> getActivePlayers() {
List<TournamentPlayer> activePlayers = new ArrayList<>();
@ -456,6 +462,10 @@ public abstract class TournamentImpl implements Tournament {
options.getMatchOptions().getPlayerTypes().add(pair.getPlayer2().getPlayerType());
tableEventSource.fireTableEvent(EventType.START_MATCH, pair, options.getMatchOptions());
}
public void playMultiPlayerMatch(MultiplayerRound round) {
tableEventSource.fireTableEvent(EventType.START_MULTIPLAYER_MATCH, round, options.getMatchOptions());
}
public void end() {
endTime = new Date();

View file

@ -41,15 +41,16 @@ public class TournamentOptions implements Serializable {
protected String name;
protected String tournamentType;
protected List<String> playerTypes = new ArrayList<>();
protected MatchOptions matchOptions = new MatchOptions("", "Two Player Duel");
protected MatchOptions matchOptions;
protected LimitedOptions limitedOptions;
protected boolean watchingAllowed = true;
protected int numberRounds;
protected String password;
protected int quitRatio;
public TournamentOptions(String name) {
public TournamentOptions(String name, String matchType, int numSeats) {
this.name = name;
this.matchOptions = new MatchOptions("", matchType, numSeats > 2, numSeats);
}
public String getName() {

View file

@ -34,8 +34,8 @@ package mage.game.tournament;
*/
public class TournamentSealedOptions extends TournamentOptions {
public TournamentSealedOptions(String name) {
super(name);
public TournamentSealedOptions(String name, String matchType, int numSeats) {
super(name, matchType, numSeats);
}
}

View file

@ -28,6 +28,7 @@
package mage.game.tournament;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.game.events.TableEvent;
@ -50,16 +51,25 @@ public abstract class TournamentSingleElimination extends TournamentImpl {
entry.getValue().setResults("Auto Eliminated");
}
}
while (this.getActivePlayers().size() > 1) {
// check if some player got killed / disconnected meanwhile and update their state
tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS);
Round round = createRoundRandom();
playRound(round);
eliminatePlayers(round);
if (options.matchOptions.getNumSeats() == 2) {
while (this.getActivePlayers().size() > 1) {
// check if some player got killed / disconnected meanwhile and update their state
tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS);
Round round = createRoundRandom();
playRound(round);
eliminatePlayers(round);
}
} else {
MultiplayerRound round = new MultiplayerRound(0, this, options.matchOptions.getNumSeats());
for (TournamentPlayer player : getActivePlayers()) {
round.addPlayer(player);
}
playMultiplayerRound(round);
}
nextStep();
}
private void eliminatePlayers(Round round) {
for (TournamentPairing pair: round.getPairs()) {
pair.eliminatePlayers();

View file

@ -56,13 +56,19 @@ public abstract class TournamentSwiss extends TournamentImpl {
}
}
while (this.getActivePlayers().size() > 1 && this.getNumberRounds() > this.getRounds().size()) {
// check if some player got killed / disconnected meanwhile and update their state
tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS);
// Swiss pairing
Round round = createRoundSwiss();
playRound(round);
if (options.matchOptions.getNumSeats() == 2) {
while (this.getActivePlayers().size() > 1 && this.getNumberRounds() > this.getRounds().size()) {
// check if some player got killed / disconnected meanwhile and update their state
tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS);
// Swiss pairing
Round round = createRoundSwiss();
playRound(round);
}
} else {
MultiplayerRound round = createMultiplayerRound();
playMultiplayerRound(round);
}
nextStep();
}
@ -70,33 +76,60 @@ public abstract class TournamentSwiss extends TournamentImpl {
List<TournamentPlayer> roundPlayers = getActivePlayers();
boolean isLastRound = (rounds.size() + 1 == getNumberRounds());
RoundPairings roundPairings;
if (roundPlayers.size() <= 16) {
SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound);
roundPairings = swissPairing.getRoundPairings();
} else {
SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds);
roundPairings = swissPairing.getRoundPairings();
}
Round round = new Round(rounds.size() + 1, this);
rounds.add(round);
for (TournamentPairing pairing : roundPairings.getPairings()) {
round.addPairing(pairing);
}
for (TournamentPlayer playerBye : roundPairings.getPlayerByes()) {
// player free round - add to bye players of this round
round.getPlayerByes().add(playerBye);
if (isLastRound) {
playerBye.setState(TournamentPlayerState.FINISHED);
Round round = null;
if (options.matchOptions.getNumSeats() == 2) {
RoundPairings roundPairings;
if (roundPlayers.size() <= 16) {
SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound);
roundPairings = swissPairing.getRoundPairings();
} else {
playerBye.setState(TournamentPlayerState.WAITING);
SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds);
roundPairings = swissPairing.getRoundPairings();
}
round = new Round(rounds.size() + 1, this);
rounds.add(round);
for (TournamentPairing pairing : roundPairings.getPairings()) {
round.addPairing(pairing);
}
for (TournamentPlayer playerBye : roundPairings.getPlayerByes()) {
// player free round - add to bye players of this round
round.getPlayerByes().add(playerBye);
if (isLastRound) {
playerBye.setState(TournamentPlayerState.FINISHED);
} else {
playerBye.setState(TournamentPlayerState.WAITING);
}
playerBye.setStateInfo("Round Bye");
updateResults();
}
playerBye.setStateInfo("Round Bye");
updateResults();
}
return round;
}
public MultiplayerRound createMultiplayerRound() {
List<TournamentPlayer> roundPlayers = getActivePlayers();
boolean isLastRound = (rounds.size() + 1 == getNumberRounds());
MultiplayerRound round = null;
if (options.matchOptions.getNumSeats() > 2) {
RoundPairings roundPairings;
if (roundPlayers.size() <= 16) {
SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound);
roundPairings = swissPairing.getRoundPairings();
} else {
SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds);
roundPairings = swissPairing.getRoundPairings();
}
round = new MultiplayerRound(rounds.size() + 1, this, options.matchOptions.getNumSeats());
for (TournamentPairing pairing : roundPairings.getPairings()) {
round.addPairing(pairing);
}
}
return round;
}
}

View file

@ -2870,7 +2870,7 @@ public abstract class PlayerImpl implements Player, Serializable {
for (Mode mode : option.getModes().values()) {
Ability newOption = option.copy();
newOption.getModes().getSelectedModes().clear();
newOption.getModes().getSelectedModes().add(mode);
newOption.getModes().getSelectedModes().add(mode.getId());
newOption.getModes().setActiveMode(mode);
if (newOption.getTargets().getUnchosen().size() > 0) {
if (newOption.getManaCosts().getVariableCosts().size() > 0) {

View file

@ -25,10 +25,13 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.target.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPermanent;
/**
@ -38,7 +41,7 @@ import mage.target.TargetPermanent;
public class TargetCreatureOrPlaneswalker extends TargetPermanent {
public TargetCreatureOrPlaneswalker() {
this(1, 1 ,new FilterCreatureOrPlaneswalkerPermanent(), false);
this(1, 1, new FilterCreatureOrPlaneswalkerPermanent(), false);
}
public TargetCreatureOrPlaneswalker(int minNumTargets, int maxNumTargets, FilterCreatureOrPlaneswalkerPermanent filter, boolean notTarget) {
@ -55,4 +58,16 @@ public class TargetCreatureOrPlaneswalker extends TargetPermanent {
return new TargetCreatureOrPlaneswalker(this);
}
@Override
public boolean isLegal(Ability source, Game game) {
for (UUID playerId : targets.keySet()) {
Player targetPlayer = game.getPlayer(playerId);
if (targetPlayer != null) {
// there seems to be no possibility to add more predicates for theplayer so return here true
return true;
}
}
return super.isLegal(source, game); //To change body of generated methods, choose Tools | Templates.
}
}

View file

@ -70,7 +70,7 @@ public class TargetAddress {
protected Iterator<SpellAbility> spellAbilityIterator;
protected Integer lastSpellAbilityIndex = null;
protected Iterator<Mode> modeIterator = null;
protected Iterator<UUID> modeIterator = null;
protected Modes modes = null;
protected UUID lastMode = null;
protected Iterator<Target> targetIterator = null;
@ -127,7 +127,7 @@ public class TargetAddress {
}
if (modeIterator != null && modeIterator.hasNext()) {
lastMode = modeIterator.next().getId();
lastMode = modeIterator.next();
targetIterator = modes.get(lastMode).getTargets().iterator();
} else {
lastMode = null;

View file

@ -102,6 +102,9 @@ git log 79f8617cd3c997d89770094d7a44294b0a48731f..head --diff-filter=A --name-st
since 1.4.15v2
git log d9c804602ea116d80a74d53eaf07ee7a15cd7d81..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt
since 1.4.15v5
git log 73a2ccda9b36552a09cb2b6a5aef37559866d7fc..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt
3. Copy added_cards.txt to trunk\Utils folder
4. Run script:
> perl extract_in_wiki_format.perl