mirror of
https://github.com/correl/mage.git
synced 2024-12-25 11:11:16 +00:00
Merge branch 'master' of https://github.com/magefree/mage
This commit is contained in:
commit
3fa2deaa64
60 changed files with 890 additions and 353 deletions
|
@ -22,7 +22,7 @@ import mage.game.command.emblems.AjaniAdversaryOfTyrantsEmblem;
|
||||||
import mage.game.command.planes.AkoumPlane;
|
import mage.game.command.planes.AkoumPlane;
|
||||||
import mage.game.match.MatchType;
|
import mage.game.match.MatchType;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import mage.game.permanent.PermanentCard;
|
import mage.game.permanent.PermanentCard;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.players.StubPlayer;
|
import mage.players.StubPlayer;
|
||||||
|
@ -113,7 +113,7 @@ public class TestCardRenderDialog extends MageDialog {
|
||||||
cardsPanel.cleanUp();
|
cardsPanel.cleanUp();
|
||||||
cardsPanel.setCustomRenderMode(comboRenderMode.getSelectedIndex());
|
cardsPanel.setCustomRenderMode(comboRenderMode.getSelectedIndex());
|
||||||
|
|
||||||
Game game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new TestGame(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
Player player = new StubPlayer("player1", RangeOfInfluence.ALL);
|
Player player = new StubPlayer("player1", RangeOfInfluence.ALL);
|
||||||
Deck deck = new Deck();
|
Deck deck = new Deck();
|
||||||
game.addPlayer(player, deck);
|
game.addPlayer(player, deck);
|
||||||
|
|
|
@ -13,9 +13,9 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
|
||||||
public static final int MAGE_VERSION_MINOR = 4;
|
public static final int MAGE_VERSION_MINOR = 4;
|
||||||
public static final int MAGE_VERSION_PATCH = 37;
|
public static final int MAGE_VERSION_PATCH = 37;
|
||||||
public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0
|
public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0
|
||||||
public static final String MAGE_VERSION_MINOR_PATCH = "V3"; // default
|
public static final String MAGE_VERSION_MINOR_PATCH = "V4"; // default
|
||||||
// strict mode
|
// strict mode
|
||||||
private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = false; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
|
private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
|
||||||
|
|
||||||
public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true;
|
public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true;
|
||||||
private final int major;
|
private final int major;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -6,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author spjspj
|
* @author spjspj
|
||||||
*/
|
*/
|
||||||
public class BrawlDuelMatch extends MatchImpl {
|
public class BrawlDuelMatch extends MatchImpl {
|
||||||
|
@ -22,8 +20,6 @@ public class BrawlDuelMatch extends MatchImpl {
|
||||||
BrawlDuel game = new BrawlDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
BrawlDuel game = new BrawlDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setCheckCommanderDamage(false);
|
game.setCheckCommanderDamage(false);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(true);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -6,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author spjspj
|
* @author spjspj
|
||||||
*/
|
*/
|
||||||
public class BrawlFreeForAllMatch extends MatchImpl {
|
public class BrawlFreeForAllMatch extends MatchImpl {
|
||||||
|
@ -21,9 +19,7 @@ public class BrawlFreeForAllMatch extends MatchImpl {
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
BrawlFreeForAll game = new BrawlFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
BrawlFreeForAll game = new BrawlFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(true);
|
|
||||||
game.setCheckCommanderDamage(false);
|
game.setCheckCommanderDamage(false);
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -6,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public class CommanderDuelMatch extends MatchImpl {
|
public class CommanderDuelMatch extends MatchImpl {
|
||||||
|
@ -18,24 +16,19 @@ public class CommanderDuelMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 40;
|
int startLife = 40;
|
||||||
boolean alsoHand = true;
|
|
||||||
// Don't like it to compare but seems like it's complicated to do it in another way
|
// Don't like it to compare but seems like it's complicated to do it in another way
|
||||||
boolean checkCommanderDamage = true;
|
boolean checkCommanderDamage = true;
|
||||||
if (options.getDeckType().equals("Variant Magic - Duel Commander")) {
|
if (options.getDeckType().equals("Variant Magic - Duel Commander")) {
|
||||||
startLife = 20; // Starting with the Commander 2016 update (on November 11th, 2016), Duel Commander will be played with 20 life points instead of 30.
|
startLife = 20; // Starting with the Commander 2016 update (on November 11th, 2016), Duel Commander will be played with 20 life points instead of 30.
|
||||||
alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015
|
|
||||||
checkCommanderDamage = false; // since nov 16 duel commander uses no longer commander damage rule
|
checkCommanderDamage = false; // since nov 16 duel commander uses no longer commander damage rule
|
||||||
}
|
}
|
||||||
if (options.getDeckType().equals("Variant Magic - MTGO 1v1 Commander")) {
|
if (options.getDeckType().equals("Variant Magic - MTGO 1v1 Commander")) {
|
||||||
startLife = 30;
|
startLife = 30;
|
||||||
alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015
|
|
||||||
}
|
}
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
CommanderDuel game = new CommanderDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
CommanderDuel game = new CommanderDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setCheckCommanderDamage(checkCommanderDamage);
|
game.setCheckCommanderDamage(checkCommanderDamage);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -7,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class CommanderFreeForAllMatch extends MatchImpl {
|
public class CommanderFreeForAllMatch extends MatchImpl {
|
||||||
|
@ -19,16 +16,12 @@ public class CommanderFreeForAllMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 40;
|
int startLife = 40;
|
||||||
boolean alsoHand = true;
|
|
||||||
if (options.getDeckType().equals("Variant Magic - Duel Commander")) {
|
if (options.getDeckType().equals("Variant Magic - Duel Commander")) {
|
||||||
startLife = 30;
|
startLife = 30;
|
||||||
alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015
|
|
||||||
}
|
}
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
CommanderFreeForAll game = new CommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
CommanderFreeForAll game = new CommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,11 @@ public class FreeformCommanderDuelMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 20;
|
int startLife = 20;
|
||||||
boolean alsoHand = true;
|
|
||||||
boolean checkCommanderDamage = true;
|
|
||||||
|
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
FreeformCommanderDuel game = new FreeformCommanderDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
FreeformCommanderDuel game = new FreeformCommanderDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setCheckCommanderDamage(checkCommanderDamage);
|
game.setCheckCommanderDamage(true);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -6,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author spjspj
|
* @author spjspj
|
||||||
*/
|
*/
|
||||||
public class FreeformCommanderFreeForAllMatch extends MatchImpl {
|
public class FreeformCommanderFreeForAllMatch extends MatchImpl {
|
||||||
|
@ -18,12 +16,9 @@ public class FreeformCommanderFreeForAllMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 40;
|
int startLife = 40;
|
||||||
boolean alsoHand = true;
|
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
FreeformCommanderFreeForAll game = new FreeformCommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
FreeformCommanderFreeForAll game = new FreeformCommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.match.MatchType;
|
import mage.game.match.MatchType;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author JayDi85
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
|
@ -12,6 +14,7 @@ public class OathbreakerDuel extends OathbreakerFreeForAll {
|
||||||
|
|
||||||
public OathbreakerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
public OathbreakerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
||||||
super(attackOption, range, mulligan, startLife);
|
super(attackOption, range, mulligan, startLife);
|
||||||
|
this.startingPlayerSkipsDraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OathbreakerDuel(final OathbreakerDuel game) {
|
public OathbreakerDuel(final OathbreakerDuel game) {
|
||||||
|
@ -33,4 +36,11 @@ public class OathbreakerDuel extends OathbreakerFreeForAll {
|
||||||
return new OathbreakerDuel(this);
|
return new OathbreakerDuel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(UUID choosingPlayerId) {
|
||||||
|
super.init(choosingPlayerId);
|
||||||
|
|
||||||
|
startingPlayerSkipsDraw = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,10 @@ public class OathbreakerDuelMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 20;
|
int startLife = 20;
|
||||||
boolean alsoHand = true;
|
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
OathbreakerDuel game = new OathbreakerDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
OathbreakerDuel game = new OathbreakerDuel(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setCheckCommanderDamage(false);
|
game.setCheckCommanderDamage(false);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
|
||||||
|
|
||||||
public OathbreakerFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
public OathbreakerFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
||||||
super(attackOption, range, mulligan, startLife);
|
super(attackOption, range, mulligan, startLife);
|
||||||
|
this.startingPlayerSkipsDraw = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OathbreakerFreeForAll(final OathbreakerFreeForAll game) {
|
public OathbreakerFreeForAll(final OathbreakerFreeForAll game) {
|
||||||
|
@ -42,13 +43,6 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
|
||||||
game.playerOathbreakers.forEach((key, value) -> this.playerOathbreakers.put(key, new HashSet<>(value)));
|
game.playerOathbreakers.forEach((key, value) -> this.playerOathbreakers.put(key, new HashSet<>(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init(UUID choosingPlayerId) {
|
|
||||||
// init base commander game
|
|
||||||
startingPlayerSkipsDraw = false;
|
|
||||||
super.init(choosingPlayerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCommanderTypeName(Card commander) {
|
private String getCommanderTypeName(Card commander) {
|
||||||
return commander.isInstantOrSorcery() ? COMMANDER_NAME_SIGNATURE_SPELL : COMMANDER_NAME_OATHBREAKER;
|
return commander.isInstantOrSorcery() ? COMMANDER_NAME_SIGNATURE_SPELL : COMMANDER_NAME_OATHBREAKER;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,10 @@ public class OathbreakerFreeForAllMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 20;
|
int startLife = 20;
|
||||||
boolean alsoHand = true;
|
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
OathbreakerFreeForAll game = new OathbreakerFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
OathbreakerFreeForAll game = new OathbreakerFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setCheckCommanderDamage(false);
|
game.setCheckCommanderDamage(false);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
import mage.game.match.MatchImpl;
|
import mage.game.match.MatchImpl;
|
||||||
|
@ -7,7 +5,6 @@ import mage.game.match.MatchOptions;
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author spjspj
|
* @author spjspj
|
||||||
*/
|
*/
|
||||||
public class PennyDreadfulCommanderFreeForAllMatch extends MatchImpl {
|
public class PennyDreadfulCommanderFreeForAllMatch extends MatchImpl {
|
||||||
|
@ -19,16 +16,12 @@ public class PennyDreadfulCommanderFreeForAllMatch extends MatchImpl {
|
||||||
@Override
|
@Override
|
||||||
public void startGame() throws GameException {
|
public void startGame() throws GameException {
|
||||||
int startLife = 40;
|
int startLife = 40;
|
||||||
boolean alsoHand = true;
|
|
||||||
if (options.getDeckType().equals("Variant Magic - Duel Penny Dreadful Commander")) {
|
if (options.getDeckType().equals("Variant Magic - Duel Penny Dreadful Commander")) {
|
||||||
startLife = 30;
|
startLife = 30;
|
||||||
alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015
|
|
||||||
}
|
}
|
||||||
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
Mulligan mulligan = options.getMulliganType().getMulligan(options.getFreeMulligans());
|
||||||
PennyDreadfulCommanderFreeForAll game = new PennyDreadfulCommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
PennyDreadfulCommanderFreeForAll game = new PennyDreadfulCommanderFreeForAll(options.getAttackOption(), options.getRange(), mulligan, startLife);
|
||||||
game.setStartMessage(this.createGameStartMessage());
|
game.setStartMessage(this.createGameStartMessage());
|
||||||
game.setAlsoHand(alsoHand);
|
|
||||||
game.setAlsoLibrary(true);
|
|
||||||
initGame(game);
|
initGame(game);
|
||||||
games.add(game);
|
games.add(game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package mage.player.ai;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
import mage.abilities.ActivatedAbility;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.StaticAbility;
|
||||||
import mage.abilities.common.PassAbility;
|
import mage.abilities.common.PassAbility;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.SearchEffect;
|
import mage.abilities.effects.SearchEffect;
|
||||||
|
@ -36,7 +37,6 @@ import org.apache.log4j.Logger;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import mage.abilities.StaticAbility;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nantuko
|
* @author nantuko
|
||||||
|
@ -794,72 +794,100 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
private void declareAttackers(Game game, UUID activePlayerId) {
|
private void declareAttackers(Game game, UUID activePlayerId) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, activePlayerId));
|
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, activePlayerId));
|
||||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) {
|
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) {
|
||||||
|
|
||||||
Player attackingPlayer = game.getPlayer(activePlayerId);
|
Player attackingPlayer = game.getPlayer(activePlayerId);
|
||||||
// TODO: this works only in two player game, also no attack of Planeswalker
|
|
||||||
UUID defenderId = game.getOpponents(playerId).iterator().next();
|
|
||||||
Player defender = game.getPlayer(defenderId);
|
|
||||||
|
|
||||||
List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
|
// TODO: add attack of Planeswalker
|
||||||
if (attackersList.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Permanent> possibleBlockers = defender.getAvailableBlockers(game);
|
// 1. check alpha strike first (all in attack to kill)
|
||||||
|
for (UUID defenderId : game.getOpponents(playerId)) {
|
||||||
List<Permanent> killers = CombatUtil.canKillOpponent(game, attackersList, possibleBlockers, defender);
|
Player defender = game.getPlayer(defenderId);
|
||||||
if (!killers.isEmpty()) {
|
if (!defender.isInGame()) {
|
||||||
for (Permanent attacker : killers) {
|
continue;
|
||||||
attackingPlayer.declareAttacker(attacker.getId(), defenderId, game, false);
|
}
|
||||||
|
|
||||||
|
List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
|
||||||
|
if (attackersList.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Permanent> possibleBlockers = defender.getAvailableBlockers(game);
|
||||||
|
List<Permanent> killers = CombatUtil.canKillOpponent(game, attackersList, possibleBlockers, defender);
|
||||||
|
if (!killers.isEmpty()) {
|
||||||
|
for (Permanent attacker : killers) {
|
||||||
|
attackingPlayer.declareAttacker(attacker.getId(), defenderId, game, false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The AI will now attack more sanely. Simple, but good enough for now.
|
// 2. check all other actions
|
||||||
// The sim minmax does not work at the moment.
|
for (UUID defenderId : game.getOpponents(playerId)) {
|
||||||
boolean safeToAttack;
|
Player defender = game.getPlayer(defenderId);
|
||||||
CombatEvaluator eval = new CombatEvaluator();
|
if (!defender.isInGame()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
|
||||||
|
if (attackersList.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Permanent> possibleBlockers = defender.getAvailableBlockers(game);
|
||||||
|
|
||||||
for (Permanent attacker : attackersList) {
|
// The AI will now attack more sanely. Simple, but good enough for now.
|
||||||
safeToAttack = true;
|
// The sim minmax does not work at the moment.
|
||||||
int attackerValue = eval.evaluate(attacker, game);
|
boolean safeToAttack;
|
||||||
for (Permanent blocker : possibleBlockers) {
|
CombatEvaluator eval = new CombatEvaluator();
|
||||||
int blockerValue = eval.evaluate(blocker, game);
|
|
||||||
if (attacker.getPower().getValue() <= blocker.getToughness().getValue()
|
for (Permanent attacker : attackersList) {
|
||||||
&& attacker.getToughness().getValue() <= blocker.getPower().getValue()) {
|
safeToAttack = true;
|
||||||
safeToAttack = false;
|
int attackerValue = eval.evaluate(attacker, game);
|
||||||
}
|
for (Permanent blocker : possibleBlockers) {
|
||||||
if (attacker.getToughness().getValue() == blocker.getPower().getValue()
|
int blockerValue = eval.evaluate(blocker, game);
|
||||||
&& attacker.getPower().getValue() == blocker.getToughness().getValue()) {
|
|
||||||
if (attackerValue > blockerValue
|
// blocker can kill attacker
|
||||||
|| blocker.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())
|
if (attacker.getPower().getValue() <= blocker.getToughness().getValue()
|
||||||
|| blocker.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())
|
&& attacker.getToughness().getValue() <= blocker.getPower().getValue()) {
|
||||||
|| blocker.getAbilities().contains(new ExaltedAbility())
|
|
||||||
|| blocker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())
|
|
||||||
|| blocker.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())
|
|
||||||
|| !attacker.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())
|
|
||||||
|| !attacker.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())
|
|
||||||
|| !attacker.getAbilities().contains(new ExaltedAbility())) {
|
|
||||||
safeToAttack = false;
|
safeToAttack = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// kill each other
|
||||||
|
if (attacker.getToughness().getValue() == blocker.getPower().getValue()
|
||||||
|
&& attacker.getPower().getValue() == blocker.getToughness().getValue()) {
|
||||||
|
if (attackerValue > blockerValue
|
||||||
|
|| blocker.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())
|
||||||
|
|| blocker.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())
|
||||||
|
|| blocker.getAbilities().contains(new ExaltedAbility())
|
||||||
|
|| blocker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())
|
||||||
|
|| blocker.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())
|
||||||
|
|| !attacker.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())
|
||||||
|
|| !attacker.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())
|
||||||
|
|| !attacker.getAbilities().contains(new ExaltedAbility())) {
|
||||||
|
safeToAttack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attacker can kill by deathtouch
|
||||||
|
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())
|
||||||
|
|| attacker.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())) {
|
||||||
|
safeToAttack = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attacker can ignore blocker
|
||||||
|
if (attacker.getAbilities().containsKey(FlyingAbility.getInstance().getId())
|
||||||
|
&& !blocker.getAbilities().containsKey(FlyingAbility.getInstance().getId())
|
||||||
|
&& !blocker.getAbilities().containsKey(ReachAbility.getInstance().getId())) {
|
||||||
|
safeToAttack = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())
|
|
||||||
|| attacker.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())) {
|
// 0 damage
|
||||||
safeToAttack = true;
|
if (attacker.getPower().getValue() == 0) {
|
||||||
|
safeToAttack = false;
|
||||||
}
|
}
|
||||||
if (attacker.getAbilities().containsKey(FlyingAbility.getInstance().getId())
|
|
||||||
&& !blocker.getAbilities().containsKey(FlyingAbility.getInstance().getId())
|
if (safeToAttack) {
|
||||||
&& !blocker.getAbilities().containsKey(ReachAbility.getInstance().getId())) {
|
// undo has to be possible e.g. if not able to pay a attack fee (e.g. Ghostly Prison)
|
||||||
safeToAttack = true;
|
attackingPlayer.declareAttacker(attacker.getId(), defenderId, game, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attacker.getPower().getValue() == 0) {
|
|
||||||
safeToAttack = false;
|
|
||||||
}
|
|
||||||
if (safeToAttack) {
|
|
||||||
// undo has to be possible e.g. if not able to pay a attack fee (e.g. Ghostly Prison)
|
|
||||||
attackingPlayer.declareAttacker(attacker.getId(), defenderId, game, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public final class CombatUtil {
|
||||||
|
|
||||||
public static List<Permanent> canKillOpponent(Game game, List<Permanent> attackersList, List<Permanent> blockersList,
|
public static List<Permanent> canKillOpponent(Game game, List<Permanent> attackersList, List<Permanent> blockersList,
|
||||||
Player defender) {
|
Player defender) {
|
||||||
List<Permanent> blockableAttackers = new ArrayList<>(blockersList);
|
List<Permanent> blockableAttackers = new ArrayList<>(attackersList);
|
||||||
List<Permanent> unblockableAttackers = new ArrayList<>();
|
List<Permanent> unblockableAttackers = new ArrayList<>();
|
||||||
for (Permanent attacker : attackersList) {
|
for (Permanent attacker : attackersList) {
|
||||||
if (!canBeBlocked(game, attacker, blockersList)) {
|
if (!canBeBlocked(game, attacker, blockersList)) {
|
||||||
|
@ -292,7 +292,7 @@ public final class CombatUtil {
|
||||||
}
|
}
|
||||||
return canBlock;
|
return canBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CombatInfo blockWithGoodTrade2(Game game, List<Permanent> attackers, List<Permanent> blockers) {
|
public static CombatInfo blockWithGoodTrade2(Game game, List<Permanent> attackers, List<Permanent> blockers) {
|
||||||
|
|
||||||
UUID attackerId = game.getCombat().getAttackingPlayerId();
|
UUID attackerId = game.getCombat().getAttackingPlayerId();
|
||||||
|
@ -319,7 +319,7 @@ public final class CombatUtil {
|
||||||
|
|
||||||
return combatInfo;
|
return combatInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Permanent> getBlockersThatWillSurvive2(Game game, UUID attackerId, UUID defenderId, Permanent attacker, List<Permanent> possibleBlockers) {
|
private static List<Permanent> getBlockersThatWillSurvive2(Game game, UUID attackerId, UUID defenderId, Permanent attacker, List<Permanent> possibleBlockers) {
|
||||||
List<Permanent> blockers = new ArrayList<>();
|
List<Permanent> blockers = new ArrayList<>();
|
||||||
for (Permanent blocker : possibleBlockers) {
|
for (Permanent blocker : possibleBlockers) {
|
||||||
|
@ -335,9 +335,9 @@ public final class CombatUtil {
|
||||||
}
|
}
|
||||||
return blockers;
|
return blockers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SurviveInfo willItSurvive2(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
public static SurviveInfo willItSurvive2(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||||
|
|
||||||
Game sim = game.copy();
|
Game sim = game.copy();
|
||||||
|
|
||||||
Combat combat = sim.getCombat();
|
Combat combat = sim.getCombat();
|
||||||
|
@ -347,7 +347,7 @@ public final class CombatUtil {
|
||||||
if (blocker == null || attacker == null || sim.getPlayer(defendingPlayerId) == null) {
|
if (blocker == null || attacker == null || sim.getPlayer(defendingPlayerId) == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker.getPower().getValue() >= blocker.getToughness().getValue()) {
|
if (attacker.getPower().getValue() >= blocker.getToughness().getValue()) {
|
||||||
sim.getBattlefield().removePermanent(blocker.getId());
|
sim.getBattlefield().removePermanent(blocker.getId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1993,12 +1993,14 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PASS_PRIORITY_UNTIL_STACK_RESOLVED:
|
case PASS_PRIORITY_UNTIL_STACK_RESOLVED:
|
||||||
|
// stop recording only, real stack processing in PlayerImpl
|
||||||
if (recordingMacro) {
|
if (recordingMacro) {
|
||||||
logger.debug("Adding a resolveStack");
|
logger.debug("Adding a resolveStack");
|
||||||
PlayerResponse tResponse = new PlayerResponse();
|
PlayerResponse tResponse = new PlayerResponse();
|
||||||
tResponse.setString("resolveStack");
|
tResponse.setString("resolveStack");
|
||||||
actionQueueSaved.add(tResponse);
|
actionQueueSaved.add(tResponse);
|
||||||
}
|
}
|
||||||
|
super.sendPlayerAction(playerAction, game, data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.sendPlayerAction(playerAction, game, data);
|
super.sendPlayerAction(playerAction, game, data);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
@ -16,6 +14,8 @@ import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterNonlandCard;
|
import mage.filter.common.FilterNonlandCard;
|
||||||
|
import mage.filter.predicate.Predicates;
|
||||||
|
import mage.filter.predicate.mageobject.CardIdPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.game.events.GameEvent.EventType;
|
||||||
|
@ -26,8 +26,9 @@ import mage.target.common.TargetCardInExile;
|
||||||
import mage.target.targetpointer.FixedTarget;
|
import mage.target.targetpointer.FixedTarget;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public final class KnowledgePool extends CardImpl {
|
public final class KnowledgePool extends CardImpl {
|
||||||
|
@ -148,13 +149,15 @@ class KnowledgePoolEffect2 extends OneShotEffect {
|
||||||
if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) {
|
if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) {
|
||||||
Player player = game.getPlayer(spell.getControllerId());
|
Player player = game.getPlayer(spell.getControllerId());
|
||||||
if (player != null && player.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) {
|
if (player != null && player.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) {
|
||||||
TargetCardInExile target = new TargetCardInExile(filter, source.getSourceId());
|
FilterNonlandCard realFilter = filter.copy();
|
||||||
while (player.choose(Outcome.PlayForFree, game.getExile().getExileZone(exileZoneId), target, game)) {
|
realFilter.add(Predicates.not(new CardIdPredicate(spell.getSourceId())));
|
||||||
|
TargetCardInExile target = new TargetCardInExile(0, 1, realFilter, source.getSourceId());
|
||||||
|
target.setNotTarget(true);
|
||||||
|
if (player.choose(Outcome.PlayForFree, game.getExile().getExileZone(exileZoneId), target, game)) {
|
||||||
Card card = game.getCard(target.getFirstTarget());
|
Card card = game.getCard(target.getFirstTarget());
|
||||||
if (card != null && !card.getId().equals(spell.getSourceId())) {
|
if (card != null && !card.getId().equals(spell.getSourceId())) {
|
||||||
return player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
|
player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
|
||||||
}
|
}
|
||||||
target.clearChosen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.AsThoughEffectImpl;
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
|
@ -9,11 +8,7 @@ import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.AsThoughEffectType;
|
import mage.constants.*;
|
||||||
import mage.constants.CardType;
|
|
||||||
import mage.constants.Duration;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.game.ExileZone;
|
import mage.game.ExileZone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
@ -21,14 +16,15 @@ import mage.target.common.TargetCardInLibrary;
|
||||||
import mage.target.common.TargetOpponent;
|
import mage.target.common.TargetOpponent;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author BetaSteward
|
* @author BetaSteward
|
||||||
*/
|
*/
|
||||||
public final class PraetorsGrasp extends CardImpl {
|
public final class PraetorsGrasp extends CardImpl {
|
||||||
|
|
||||||
public PraetorsGrasp(UUID ownerId, CardSetInfo setInfo) {
|
public PraetorsGrasp(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}");
|
||||||
|
|
||||||
// Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled.
|
// Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled.
|
||||||
this.getSpellAbility().addEffect(new PraetorsGraspEffect());
|
this.getSpellAbility().addEffect(new PraetorsGraspEffect());
|
||||||
|
@ -120,11 +116,7 @@ class PraetorsGraspPlayEffect extends AsThoughEffectImpl {
|
||||||
if (exileId != null && controller != null) {
|
if (exileId != null && controller != null) {
|
||||||
ExileZone exileZone = game.getExile().getExileZone(exileId);
|
ExileZone exileZone = game.getExile().getExileZone(exileId);
|
||||||
if (exileZone != null && exileZone.contains(cardId)) {
|
if (exileZone != null && exileZone.contains(cardId)) {
|
||||||
if (controller.chooseUse(outcome, "Play the exiled card?", source, game)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
discard();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package mage.cards.t;
|
package mage.cards.t;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.Mode;
|
||||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.hint.ValueHint;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.WatcherScope;
|
import mage.constants.WatcherScope;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterInstantOrSorcerySpell;
|
import mage.filter.common.FilterInstantOrSorcerySpell;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
|
import mage.players.Player;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -28,9 +34,7 @@ public final class ThousandYearStorm extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{R}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{R}");
|
||||||
|
|
||||||
// Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies.
|
// Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies.
|
||||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
this.addAbility(new ThousandYearStormAbility());
|
||||||
new ThousandYearStormEffect(), new FilterInstantOrSorcerySpell(), false, true
|
|
||||||
), new ThousandYearWatcher());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThousandYearStorm(final ThousandYearStorm card) {
|
public ThousandYearStorm(final ThousandYearStorm card) {
|
||||||
|
@ -43,11 +47,48 @@ public final class ThousandYearStorm extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ThousandYearStormAbility extends SpellCastControllerTriggeredAbility {
|
||||||
|
|
||||||
|
String stormCountInfo = null;
|
||||||
|
|
||||||
|
public ThousandYearStormAbility() {
|
||||||
|
super(Zone.BATTLEFIELD, new ThousandYearStormEffect(), new FilterInstantOrSorcerySpell(), false, true);
|
||||||
|
this.addHint(new ValueHint("You've cast instant and sorcery this turn", ThousandYearSpellsCastThatTurnValue.instance));
|
||||||
|
this.addWatcher(new ThousandYearWatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThousandYearStormAbility(final ThousandYearStormAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
this.stormCountInfo = ability.stormCountInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
// save storm count info, real count will be calculated to stack ability in resolve effect only
|
||||||
|
if (super.checkTrigger(event, game)) {
|
||||||
|
int stormCount = ThousandYearSpellsCastThatTurnValue.instance.calculate(game, this, null);
|
||||||
|
stormCountInfo = " (<b>storm count: " + Math.max(0, stormCount - 1) + "</b>) ";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule() {
|
||||||
|
return "Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you've cast before it this turn"
|
||||||
|
+ (stormCountInfo != null ? stormCountInfo : "") + ". You may choose new targets for the copies.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThousandYearStormAbility copy() {
|
||||||
|
return new ThousandYearStormAbility(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ThousandYearStormEffect extends OneShotEffect {
|
class ThousandYearStormEffect extends OneShotEffect {
|
||||||
|
|
||||||
public ThousandYearStormEffect() {
|
public ThousandYearStormEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
this.staticText = "copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThousandYearStormEffect(final ThousandYearStormEffect effect) {
|
public ThousandYearStormEffect(final ThousandYearStormEffect effect) {
|
||||||
|
@ -62,7 +103,8 @@ class ThousandYearStormEffect extends OneShotEffect {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source));
|
Spell spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source));
|
||||||
if (spell != null) {
|
Player controller = spell != null ? game.getPlayer(spell.getControllerId()) : null;
|
||||||
|
if (spell != null && controller != null) {
|
||||||
ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class);
|
ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class);
|
||||||
if (watcher != null) {
|
if (watcher != null) {
|
||||||
String stateSearchId = spell.getId().toString() + source.getSourceId().toString();
|
String stateSearchId = spell.getId().toString() + source.getSourceId().toString();
|
||||||
|
@ -81,6 +123,11 @@ class ThousandYearStormEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText(Mode mode) {
|
||||||
|
return "copy it for each other instant and sorcery spell you've cast before it this turn. You may choose new targets for the copies";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThousandYearWatcher extends Watcher {
|
class ThousandYearWatcher extends Watcher {
|
||||||
|
@ -131,3 +178,32 @@ class ThousandYearWatcher extends Watcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ThousandYearSpellsCastThatTurnValue implements DynamicValue {
|
||||||
|
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
|
ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class);
|
||||||
|
if (watcher == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThousandYearSpellsCastThatTurnValue copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "X";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "You cast an instant or sorcery spell in this turn";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
package org.mage.test.AI.basic;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class AttackAndBlockByAITest extends CardTestPlayerBaseAI {
|
||||||
|
|
||||||
|
// only PlayerA is AI controlled
|
||||||
|
|
||||||
|
// Trove of Temptation
|
||||||
|
// Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_2_big_vs_0() {
|
||||||
|
// 2 x 2/2 vs 0 - can't lose any attackers
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // 2/2
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 2 - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_2_big_vs_1_small() {
|
||||||
|
// 2 x 2/2 vs 1 x 1/1 - can't lose any attackers
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Arbor Elf", 1); // 1/1
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 2 - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_1_big_vs_2_small() {
|
||||||
|
// 1 x 2/2 vs 2 x 1/1 - can lose 1 attacker, but will kill more opponents
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Arbor Elf", 2); // 1/1
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_2_big_vs_2_small() {
|
||||||
|
// 2 x 2/2 vs 2 x 1/1 - can lose 1 attacker, but will kill more opponents
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Arbor Elf", 2); // 1/1
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 2 - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_1_small_vs_0() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Arbor Elf", 1); // 1/1
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_1_small_vs_1_big() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Arbor Elf", 1); // 1/1
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20); // no attacks
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_2_small_vs_1_big() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Arbor Elf", 2); // 1/1
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20); // no attacks
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_15_small_vs_1_big_kill_stike() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 15); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 1); // 9/9
|
||||||
|
|
||||||
|
block(1, playerB, "Ancient Brontodon", "Balduvian Bears");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - ((15 - 1) * 2)); // one will be blocked
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore // TODO: add massive attack vs small amount of blockers
|
||||||
|
public void test_Attack_10_small_vs_1_big_massive_strike() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 10); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 1); // 9/9
|
||||||
|
|
||||||
|
block(1, playerB, "Ancient Brontodon", "Balduvian Bears");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - ((10 - 1) * 2)); // one will be blocked
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ForceAttack_1_small_vs_1_big_a() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Goblin Brigand", 1); // 1/1, force to attack
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 1); // 9/9
|
||||||
|
|
||||||
|
block(1, playerB, "Ancient Brontodon", "Goblin Brigand");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Goblin Brigand", 1);
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ForceAttack_2_small_vs_2_big() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Goblin Brigand", 2); // 1/1, force to attack
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 2); // 9/9
|
||||||
|
|
||||||
|
block(1, playerB, "Ancient Brontodon:0", "Goblin Brigand:0");
|
||||||
|
block(1, playerB, "Ancient Brontodon:1", "Goblin Brigand:1");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Goblin Brigand", 2);
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore // TODO: need to fix Trove of Temptation effect (player must attack by one creature)
|
||||||
|
public void test_ForceAttack_1_small_vs_1_big_b() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Arbor Elf", 1); // 1/1
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 1); // 9/9
|
||||||
|
// Trove of Temptation
|
||||||
|
// Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Trove of Temptation", 1); // 9/9
|
||||||
|
|
||||||
|
block(1, playerB, "Ancient Brontodon", "Arbor Elf");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Arbor Elf", 1);
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Attack_1_with_counters_vs_1() {
|
||||||
|
// chainbreaker real stats is 1/1, it's can be saftly attacked
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Chainbreaker", 1); // 3/3, but with 2x -1/-1 counters
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
assertLife(playerB, 20 - 2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.AI.basic;
|
package org.mage.test.AI.basic;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
|
@ -10,14 +9,12 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class PreventRepeatedActionsTest extends CardTestPlayerBaseAI {
|
public class PreventRepeatedActionsTest extends CardTestPlayerBaseAI {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that an equipment is not switched again an again between creatures
|
* Check that an equipment is not switched again an again between creatures
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEquipOnlyOnce() {
|
public void testEquipOnlyOnce() {
|
||||||
|
@ -77,10 +74,13 @@ public class PreventRepeatedActionsTest extends CardTestPlayerBaseAI {
|
||||||
|
|
||||||
attack(2, playerB, "Silvercoat Lion");
|
attack(2, playerB, "Silvercoat Lion");
|
||||||
attack(2, playerB, "Silvercoat Lion");
|
attack(2, playerB, "Silvercoat Lion");
|
||||||
|
blockSkip(2, playerA);
|
||||||
|
|
||||||
setStopAt(2, PhaseStep.END_TURN);
|
setStopAt(2, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Kiora's Follower", 2);
|
||||||
|
assertPermanentCount(playerB, "Silvercoat Lion", 2);
|
||||||
assertLife(playerA, 16);
|
assertLife(playerA, 16);
|
||||||
assertTapped("Kiora's Follower", false);
|
assertTapped("Kiora's Follower", false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import mage.abilities.effects.common.DamageMultiEffect;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.counters.CounterType;
|
||||||
import mage.target.common.TargetCreaturePermanentAmount;
|
import mage.target.common.TargetCreaturePermanentAmount;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -96,7 +97,70 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
||||||
assertPermanentCount(playerB, "Battering Sliver", 3);
|
assertPermanentCount(playerB, "Battering Sliver", 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
@Test
|
||||||
|
@Ignore // TODO: enable it after chooseTarget will be rewrites like choseTargetAmount
|
||||||
|
public void test_target_KillCreatureNotDamage() {
|
||||||
|
// https://github.com/magefree/mage/issues/4497
|
||||||
|
// choose target for damage selects wrong target
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Burning Sun's Avatar"); // 3 damage to target on enter
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1, true); // avatar can be cast on 3 turn
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ancient Brontodon", 1); // 9/9
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2, will be +2 counters
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Arbor Elf", 1); // 1/1
|
||||||
|
|
||||||
|
// prepare, A can't cast avatar until mana is tapped
|
||||||
|
addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", CounterType.P1P1, 2);
|
||||||
|
checkPermanentCounters("counters", 1, PhaseStep.BEGIN_COMBAT, playerB, "Balduvian Bears", CounterType.P1P1, 2);
|
||||||
|
|
||||||
|
// AI cast avatar on turn 3 and target 1 creature to kil by 3 damage
|
||||||
|
|
||||||
|
//setStrictChooseMode(true); // AI
|
||||||
|
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Burning Sun's Avatar", 1);
|
||||||
|
assertPermanentCount(playerB, "Ancient Brontodon", 1); // can't be killed
|
||||||
|
assertPermanentCount(playerB, "Balduvian Bears", 1); // 2/2, but with counters is 4/4, can't be killed
|
||||||
|
assertPermanentCount(playerB, "Arbor Elf", 0); // must be killed
|
||||||
|
assertGraveyardCount(playerB, "Arbor Elf", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore // do not enable it in production, only for devs
|
||||||
|
public void test_target_Performance() {
|
||||||
|
int cardsMultiplier = 10;
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Bolt", 1 * cardsMultiplier);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1 * cardsMultiplier);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1 * cardsMultiplier); // 1/1
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1 * cardsMultiplier); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 1 * cardsMultiplier); // 2/2 with ability
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Golden Bear", 1 * cardsMultiplier); // 4/3
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Battering Sliver", 1 * cardsMultiplier); // 4/4 with ability
|
||||||
|
|
||||||
|
//castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||||
|
|
||||||
|
showHand("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
/* disabled checks cause target is not yet fixed, see comments at the start file
|
||||||
|
assertPermanentCount(playerB, "Memnite", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Balduvian Bears", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Ashcoat Bear", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Golden Bear", 1 * cardsMultiplier - 1);
|
||||||
|
assertPermanentCount(playerB, "Battering Sliver", 1 * cardsMultiplier);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TARGET AMOUNT
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_targetAmount_PriorityKillByBigPT() {
|
public void test_targetAmount_PriorityKillByBigPT() {
|
||||||
|
@ -228,4 +292,33 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
||||||
assertPermanentCount(playerA, "Golden Bear", 3);
|
assertPermanentCount(playerA, "Golden Bear", 3);
|
||||||
assertPermanentCount(playerA, "Battering Sliver", 3);
|
assertPermanentCount(playerA, "Battering Sliver", 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore // do not enable it in production, only for devs
|
||||||
|
public void test_targetAmount_Performance() {
|
||||||
|
int cardsMultiplier = 3;
|
||||||
|
|
||||||
|
Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(3), new ManaCostsImpl("R"));
|
||||||
|
ability.addTarget(new TargetCreaturePermanentAmount(3));
|
||||||
|
addCustomCardWithAbility("damage 3", playerA, ability);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1 * cardsMultiplier); // 1/1
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1 * cardsMultiplier); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 1 * cardsMultiplier); // 2/2 with ability
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Golden Bear", 1 * cardsMultiplier); // 4/3
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Battering Sliver", 1 * cardsMultiplier); // 4/4 with ability
|
||||||
|
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: ");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Memnite", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Balduvian Bears", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Ashcoat Bear", 1 * cardsMultiplier);
|
||||||
|
assertPermanentCount(playerB, "Golden Bear", 1 * cardsMultiplier - 1);
|
||||||
|
assertPermanentCount(playerB, "Battering Sliver", 1 * cardsMultiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.AI.basic;
|
package org.mage.test.AI.basic;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
|
@ -9,7 +8,6 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class TargetsAreChosenTest extends CardTestPlayerBaseAI {
|
public class TargetsAreChosenTest extends CardTestPlayerBaseAI {
|
||||||
|
@ -158,6 +156,7 @@ public class TargetsAreChosenTest extends CardTestPlayerBaseAI {
|
||||||
// Whenever a creature an opponent controls dies, put a +1/+1 counter on Malakir Cullblade.
|
// Whenever a creature an opponent controls dies, put a +1/+1 counter on Malakir Cullblade.
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Malakir Cullblade", 5);
|
addCard(Zone.BATTLEFIELD, playerA, "Malakir Cullblade", 5);
|
||||||
|
|
||||||
|
attackSkip(1, playerA);
|
||||||
attack(3, playerA, "Nefashu");
|
attack(3, playerA, "Nefashu");
|
||||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import java.io.FileNotFoundException;
|
||||||
public class NaturesWillTest extends CardTestPlayerBase {
|
public class NaturesWillTest extends CardTestPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
playerC = createPlayer(game, playerC, "PlayerC");
|
playerC = createPlayer(game, playerC, "PlayerC");
|
||||||
|
|
|
@ -7,7 +7,7 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import java.io.FileNotFoundException;
|
||||||
public class StormTheVaultTest extends CardTestPlayerBase {
|
public class StormTheVaultTest extends CardTestPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
playerC = createPlayer(game, playerC, "PlayerC");
|
playerC = createPlayer(game, playerC, "PlayerC");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.mage.test.cards.continuous;
|
package org.mage.test.cards.continuous;
|
||||||
|
|
||||||
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -183,4 +184,91 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
execute();
|
execute();
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_CastFromHandWithoutTaxIncrease() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // cast from command for {1}{G}, from hand for {1}{G}, from command for {1}{G}{2}
|
||||||
|
//
|
||||||
|
// Counter target spell. If that spell is countered this way, put it into its owner’s hand instead of into that player’s graveyard.
|
||||||
|
// Draw a card.
|
||||||
|
addCard(Zone.HAND, playerB, "Remand", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Island", 2); // counter 2 times
|
||||||
|
|
||||||
|
// cast 1 and counter (increase commander tax)
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Remand", "Balduvian Bears", "Balduvian Bears");
|
||||||
|
setChoice(playerA, "No"); // move to hand
|
||||||
|
checkCommandCardCount("cast 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||||
|
checkHandCardCount("cast 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||||
|
checkPermanentCount("cast 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||||
|
|
||||||
|
///*
|
||||||
|
// cast 2 from hand without tax
|
||||||
|
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||||
|
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerB, "Remand", "Balduvian Bears", "Balduvian Bears");
|
||||||
|
setChoice(playerA, "Yes"); // move to command zone
|
||||||
|
checkCommandCardCount("cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||||
|
checkHandCardCount("cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||||
|
checkPermanentCount("cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||||
|
|
||||||
|
// cast 3 from command with tax for 1 play
|
||||||
|
castSpell(9, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||||
|
checkCommandCardCount("cast 3", 9, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", 0);
|
||||||
|
checkHandCardCount("cast 3", 9, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", 0);
|
||||||
|
checkPermanentCount("cast 3", 9, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", 1);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(9, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AlternativeSpellNormal() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
// Weapon Surge
|
||||||
|
// Target creature you control gets +1/+0 and gains first strike until end of turn.
|
||||||
|
// Overload {1}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of “target” with “each.”)
|
||||||
|
addCard(Zone.HAND, playerA, "Weapon Surge", 1); // {R} or {1}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2);
|
||||||
|
|
||||||
|
// cast overload
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Weapon Surge with overload");
|
||||||
|
checkAbility("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", FirstStrikeAbility.class, true);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AlternativeSpellCommander() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
// Weapon Surge
|
||||||
|
// Target creature you control gets +1/+0 and gains first strike until end of turn.
|
||||||
|
// Overload {1}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of “target” with “each.”)
|
||||||
|
addCard(Zone.COMMAND, playerA, "Weapon Surge", 1); // {R} or {1}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2);
|
||||||
|
|
||||||
|
// cast overload
|
||||||
|
showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Weapon Surge with overload");
|
||||||
|
setChoice(playerA, "Yes"); // move to command zone
|
||||||
|
checkAbility("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", FirstStrikeAbility.class, true);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.mage.test.cards.continuous;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class PraetorsGraspTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_SimpleCast() {
|
||||||
|
// Search target opponent’s library for a card and exile it face down. Then that player shuffles their library.
|
||||||
|
// You may look at and play that card for as long as it remains exiled.
|
||||||
|
addCard(Zone.HAND, playerA, "Praetor's Grasp", 1); // {1}{B}{B}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Mountain", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Praetor's Grasp");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Mountain");
|
||||||
|
|
||||||
|
showAvaileableAbilities("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerA, "Praetor's Grasp", 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package org.mage.test.cards.continuous;
|
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author JayDi85
|
|
||||||
*/
|
|
||||||
public class TroveOfTemptationTest extends CardTestPlayerBase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Ignore // TODO: 2019-04-28 - improve and uncomment test after computer player can process playerMustBeAttackedIfAble restriction
|
|
||||||
public void test_SingleOpponentMustAttack() {
|
|
||||||
// Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Trove of Temptation");
|
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); // 2/2
|
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 1); // 2/2
|
|
||||||
|
|
||||||
setStopAt(2, PhaseStep.END_TURN);
|
|
||||||
setStrictChooseMode(true);
|
|
||||||
execute();
|
|
||||||
assertAllCommandsUsed();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import java.io.FileNotFoundException;
|
||||||
public class RagsRichesTest extends CardTestMultiPlayerBase {
|
public class RagsRichesTest extends CardTestMultiPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -13,19 +12,20 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class BlatantThieveryTest extends CardTestMultiPlayerBase {
|
public class BlatantThieveryTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -8,23 +7,25 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enchantment {3}{B}
|
* Enchantment {3}{B}
|
||||||
* At the beginning of your upkeep, each player discards a card.
|
* At the beginning of your upkeep, each player discards a card.
|
||||||
* Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.
|
* Each opponent who discarded a card that shares a card type with the card you discarded loses 3 life.
|
||||||
* (Players reveal the discarded cards simultaneously.)
|
* (Players reveal the discarded cards simultaneously.)
|
||||||
*
|
*
|
||||||
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com
|
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com
|
||||||
*/
|
*/
|
||||||
public class CreepingDreadTest extends CardTestMultiPlayerBase {
|
public class CreepingDreadTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 40);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
@ -32,27 +33,27 @@ public class CreepingDreadTest extends CardTestMultiPlayerBase {
|
||||||
playerD = createPlayer(game, playerD, "PlayerD");
|
playerD = createPlayer(game, playerD, "PlayerD");
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard creature and all opponents who discard creature lose 3 life
|
* Discard creature and all opponents who discard creature lose 3 life
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void basicTest() {
|
public void basicTest() {
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
||||||
addCard(Zone.HAND, playerA, "Merfolk Looter");
|
addCard(Zone.HAND, playerA, "Merfolk Looter");
|
||||||
addCard(Zone.HAND, playerB, "Hill Giant");
|
addCard(Zone.HAND, playerB, "Hill Giant");
|
||||||
addCard(Zone.HAND, playerC, "Elite Vanguard");
|
addCard(Zone.HAND, playerC, "Elite Vanguard");
|
||||||
addCard(Zone.HAND, playerD, "Bone Saw");
|
addCard(Zone.HAND, playerD, "Bone Saw");
|
||||||
|
|
||||||
setChoice(playerA, "Merfolk Looter");
|
setChoice(playerA, "Merfolk Looter");
|
||||||
setChoice(playerB, "Hill Giant");
|
setChoice(playerB, "Hill Giant");
|
||||||
setChoice(playerC, "Elite Vanguard");
|
setChoice(playerC, "Elite Vanguard");
|
||||||
setChoice(playerD, "Bone Saw");
|
setChoice(playerD, "Bone Saw");
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.DRAW);
|
setStopAt(1, PhaseStep.DRAW);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Merfolk Looter", 1);
|
assertGraveyardCount(playerA, "Merfolk Looter", 1);
|
||||||
assertGraveyardCount(playerB, "Hill Giant", 1);
|
assertGraveyardCount(playerB, "Hill Giant", 1);
|
||||||
assertGraveyardCount(playerC, "Elite Vanguard", 1);
|
assertGraveyardCount(playerC, "Elite Vanguard", 1);
|
||||||
|
@ -63,27 +64,27 @@ public class CreepingDreadTest extends CardTestMultiPlayerBase {
|
||||||
assertLife(playerC, 37);
|
assertLife(playerC, 37);
|
||||||
assertLife(playerD, 40); // no match
|
assertLife(playerD, 40); // no match
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard Artifact Creature and all opponents who discard either an Artifact or Creature lose 3 life
|
* Discard Artifact Creature and all opponents who discard either an Artifact or Creature lose 3 life
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void twoTypesTest() {
|
public void twoTypesTest() {
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
||||||
addCard(Zone.HAND, playerA, "Bronze Sable");
|
addCard(Zone.HAND, playerA, "Bronze Sable");
|
||||||
addCard(Zone.HAND, playerB, "Hill Giant");
|
addCard(Zone.HAND, playerB, "Hill Giant");
|
||||||
addCard(Zone.HAND, playerC, "Swamp");
|
addCard(Zone.HAND, playerC, "Swamp");
|
||||||
addCard(Zone.HAND, playerD, "Bone Saw");
|
addCard(Zone.HAND, playerD, "Bone Saw");
|
||||||
|
|
||||||
setChoice(playerA, "Bronze Sable");
|
setChoice(playerA, "Bronze Sable");
|
||||||
setChoice(playerB, "Hill Giant");
|
setChoice(playerB, "Hill Giant");
|
||||||
setChoice(playerC, "Swamp");
|
setChoice(playerC, "Swamp");
|
||||||
setChoice(playerD, "Bone Saw");
|
setChoice(playerD, "Bone Saw");
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.DRAW);
|
setStopAt(1, PhaseStep.DRAW);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Bronze Sable", 1);
|
assertGraveyardCount(playerA, "Bronze Sable", 1);
|
||||||
assertGraveyardCount(playerB, "Hill Giant", 1);
|
assertGraveyardCount(playerB, "Hill Giant", 1);
|
||||||
assertGraveyardCount(playerC, "Swamp", 1);
|
assertGraveyardCount(playerC, "Swamp", 1);
|
||||||
|
@ -94,63 +95,63 @@ public class CreepingDreadTest extends CardTestMultiPlayerBase {
|
||||||
assertLife(playerC, 40); // neither
|
assertLife(playerC, 40); // neither
|
||||||
assertLife(playerD, 37); // artifact
|
assertLife(playerD, 37); // artifact
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard enchantment and no opponents discard an enchantment, so no one loses life
|
* Discard enchantment and no opponents discard an enchantment, so no one loses life
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void noMatchesTest() {
|
public void noMatchesTest() {
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
||||||
addCard(Zone.HAND, playerA, "Moat"); // enchantment
|
addCard(Zone.HAND, playerA, "Moat"); // enchantment
|
||||||
addCard(Zone.HAND, playerB, "Hill Giant");
|
addCard(Zone.HAND, playerB, "Hill Giant");
|
||||||
addCard(Zone.HAND, playerC, "Swamp");
|
addCard(Zone.HAND, playerC, "Swamp");
|
||||||
addCard(Zone.HAND, playerD, "Bone Saw");
|
addCard(Zone.HAND, playerD, "Bone Saw");
|
||||||
|
|
||||||
setChoice(playerA, "Moat");
|
setChoice(playerA, "Moat");
|
||||||
setChoice(playerB, "Hill Giant");
|
setChoice(playerB, "Hill Giant");
|
||||||
setChoice(playerC, "Swamp");
|
setChoice(playerC, "Swamp");
|
||||||
setChoice(playerD, "Bone Saw");
|
setChoice(playerD, "Bone Saw");
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.DRAW);
|
setStopAt(1, PhaseStep.DRAW);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Moat", 1);
|
assertGraveyardCount(playerA, "Moat", 1);
|
||||||
assertGraveyardCount(playerB, "Hill Giant", 1);
|
assertGraveyardCount(playerB, "Hill Giant", 1);
|
||||||
assertGraveyardCount(playerC, "Swamp", 1);
|
assertGraveyardCount(playerC, "Swamp", 1);
|
||||||
assertGraveyardCount(playerD, "Bone Saw", 1);
|
assertGraveyardCount(playerD, "Bone Saw", 1);
|
||||||
|
|
||||||
assertLife(playerA, 40); // no matches
|
assertLife(playerA, 40); // no matches
|
||||||
assertLife(playerB, 40);
|
assertLife(playerB, 40);
|
||||||
assertLife(playerC, 40);
|
assertLife(playerC, 40);
|
||||||
assertLife(playerD, 40);
|
assertLife(playerD, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upkeep player has no cards to discard, so no matches
|
* Upkeep player has no cards to discard, so no matches
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void noDiscardNoMatches() {
|
public void noDiscardNoMatches() {
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
addCard(Zone.BATTLEFIELD, playerA, "Creeping Dread");
|
||||||
addCard(Zone.HAND, playerB, "Hill Giant");
|
addCard(Zone.HAND, playerB, "Hill Giant");
|
||||||
addCard(Zone.HAND, playerC, "Swamp");
|
addCard(Zone.HAND, playerC, "Swamp");
|
||||||
addCard(Zone.HAND, playerD, "Bone Saw");
|
addCard(Zone.HAND, playerD, "Bone Saw");
|
||||||
|
|
||||||
setChoice(playerB, "Hill Giant");
|
setChoice(playerB, "Hill Giant");
|
||||||
setChoice(playerC, "Swamp");
|
setChoice(playerC, "Swamp");
|
||||||
setChoice(playerD, "Bone Saw");
|
setChoice(playerD, "Bone Saw");
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.DRAW);
|
setStopAt(1, PhaseStep.DRAW);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertGraveyardCount(playerB, "Hill Giant", 1);
|
assertGraveyardCount(playerB, "Hill Giant", 1);
|
||||||
assertGraveyardCount(playerC, "Swamp", 1);
|
assertGraveyardCount(playerC, "Swamp", 1);
|
||||||
assertGraveyardCount(playerD, "Bone Saw", 1);
|
assertGraveyardCount(playerD, "Bone Saw", 1);
|
||||||
|
|
||||||
assertLife(playerA, 40); // no matches
|
assertLife(playerA, 40); // no matches
|
||||||
assertLife(playerB, 40);
|
assertLife(playerB, 40);
|
||||||
assertLife(playerC, 40);
|
assertLife(playerC, 40);
|
||||||
assertLife(playerD, 40);
|
assertLife(playerD, 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -8,15 +7,17 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
public class MultiplayerTriggerTest extends CardTestMultiPlayerBase {
|
public class MultiplayerTriggerTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 40);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -7,7 +7,7 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ public class MyriadTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 40);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -13,13 +12,14 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class PlayerDiedStackTargetHandlingTest extends CardTestMultiPlayerBase {
|
public class PlayerDiedStackTargetHandlingTest extends CardTestMultiPlayerBase {
|
||||||
|
@ -27,7 +27,7 @@ public class PlayerDiedStackTargetHandlingTest extends CardTestMultiPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
// Start Life = 2
|
// Start Life = 2
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, new VancouverMulligan(0), 3);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 3);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -10,14 +8,15 @@ import mage.counters.CounterType;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
|
public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
|
||||||
|
@ -25,7 +24,7 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
// Start Life = 2
|
// Start Life = 2
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, new VancouverMulligan(0), 2);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 2);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
@ -131,7 +130,7 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
|
||||||
* player concedes the game and removes the Planeswalker. Once it becomes an
|
* player concedes the game and removes the Planeswalker. Once it becomes an
|
||||||
* opponent of the original player's turn and that opponent plays a spell,
|
* opponent of the original player's turn and that opponent plays a spell,
|
||||||
* Xmage throws an error and rollsback the turn.
|
* Xmage throws an error and rollsback the turn.
|
||||||
*
|
* <p>
|
||||||
* I don't have the actual error report on my due to negligence, but what I
|
* I don't have the actual error report on my due to negligence, but what I
|
||||||
* can recollect is that the error message was along the lines of "The
|
* can recollect is that the error message was along the lines of "The
|
||||||
* emblem cannot find the original source. This turn will be rolled back".
|
* emblem cannot find the original source. This turn will be rolled back".
|
||||||
|
@ -278,7 +277,7 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
|
||||||
/**
|
/**
|
||||||
* Pithing Needle keeps the named card's abilities disabled even after the
|
* Pithing Needle keeps the named card's abilities disabled even after the
|
||||||
* player controlling the Needle loses the game.
|
* player controlling the Needle loses the game.
|
||||||
*
|
* <p>
|
||||||
* I saw it happen during a Commander game. A player cast Pithing Needle
|
* I saw it happen during a Commander game. A player cast Pithing Needle
|
||||||
* targeting my Proteus Staff. After I killed him, I still couldn't activate
|
* targeting my Proteus Staff. After I killed him, I still couldn't activate
|
||||||
* the Staff.
|
* the Staff.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -9,13 +8,14 @@ import mage.counters.CounterType;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class PlayerLeftGameRangeAllTest extends CardTestMultiPlayerBase {
|
public class PlayerLeftGameRangeAllTest extends CardTestMultiPlayerBase {
|
||||||
|
@ -23,7 +23,7 @@ public class PlayerLeftGameRangeAllTest extends CardTestMultiPlayerBase {
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
// Start Life = 2
|
// Start Life = 2
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 2);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 2);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
@ -129,7 +129,7 @@ public class PlayerLeftGameRangeAllTest extends CardTestMultiPlayerBase {
|
||||||
* player concedes the game and removes the Planeswalker. Once it becomes an
|
* player concedes the game and removes the Planeswalker. Once it becomes an
|
||||||
* opponent of the original player's turn and that opponent plays a spell,
|
* opponent of the original player's turn and that opponent plays a spell,
|
||||||
* Xmage throws an error and rollsback the turn.
|
* Xmage throws an error and rollsback the turn.
|
||||||
*
|
* <p>
|
||||||
* I don't have the actual error report on my due to negligence, but what I
|
* I don't have the actual error report on my due to negligence, but what I
|
||||||
* can recollect is that the error message was along the lines of "The
|
* can recollect is that the error message was along the lines of "The
|
||||||
* emblem cannot find the original source. This turn will be rolled back".
|
* emblem cannot find the original source. This turn will be rolled back".
|
||||||
|
@ -283,7 +283,7 @@ public class PlayerLeftGameRangeAllTest extends CardTestMultiPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * 11/4/2015: In a multiplayer game, if Grasp of Fate's owner leaves the
|
* * 11/4/2015: In a multiplayer game, if Grasp of Fate's owner leaves the
|
||||||
* game, the exiled cards will return to the battlefield. Because the
|
* game, the exiled cards will return to the battlefield. Because the
|
||||||
* one-shot effect that returns the cards isn't an ability that goes on the
|
* one-shot effect that returns the cards isn't an ability that goes on the
|
||||||
* stack, it won't cease to exist along with the leaving player's spells and
|
* stack, it won't cease to exist along with the leaving player's spells and
|
||||||
|
|
|
@ -9,7 +9,7 @@ import mage.counters.CounterType;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class PrivilegedPositionTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 40);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
@ -32,9 +32,9 @@ public class PrivilegedPositionTest extends CardTestMultiPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reported bug: see issue #3328
|
* Reported bug: see issue #3328
|
||||||
* Players unable to attack Planeswalker with Privileged Position on battlefield.
|
* Players unable to attack Planeswalker with Privileged Position on battlefield.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAttackPlaneswalkerWithHexproofPrivilegedPosition() {
|
public void testAttackPlaneswalkerWithHexproofPrivilegedPosition() {
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package org.mage.test.multiplayer;
|
package org.mage.test.multiplayer;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
@ -9,19 +7,20 @@ import mage.constants.Zone;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class VindictiveLichTest extends CardTestMultiPlayerBase {
|
public class VindictiveLichTest extends CardTestMultiPlayerBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 40);
|
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -72,7 +72,9 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(TestPlayer.class);
|
private static final Logger logger = Logger.getLogger(TestPlayer.class);
|
||||||
|
|
||||||
public static final String TARGET_SKIP = "[skip]";
|
public static final String TARGET_SKIP = "[target_skip]";
|
||||||
|
public static final String BLOCK_SKIP = "[block_skip]";
|
||||||
|
public static final String ATTACK_SKIP = "[attack_skip]";
|
||||||
|
|
||||||
private int maxCallsWithoutAction = 100;
|
private int maxCallsWithoutAction = 100;
|
||||||
private int foundNoAction = 0;
|
private int foundNoAction = 0;
|
||||||
|
@ -574,7 +576,8 @@ public class TestPlayer implements Player {
|
||||||
if (permanent.getName().equals(groups[0])) {
|
if (permanent.getName().equals(groups[0])) {
|
||||||
Counter counter = new Counter(groups[1], Integer.parseInt(groups[2]));
|
Counter counter = new Counter(groups[1], Integer.parseInt(groups[2]));
|
||||||
permanent.addCounters(counter, null, game);
|
permanent.addCounters(counter, null, game);
|
||||||
break;
|
actions.remove(action);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (action.getAction().startsWith("waitStackResolved")) {
|
} else if (action.getAction().startsWith("waitStackResolved")) {
|
||||||
|
@ -901,8 +904,8 @@ public class TestPlayer implements Player {
|
||||||
.map(a -> (
|
.map(a -> (
|
||||||
a.getZone() + " -> "
|
a.getZone() + " -> "
|
||||||
+ a.getSourceObject(game).getIdName() + " -> "
|
+ a.getSourceObject(game).getIdName() + " -> "
|
||||||
+ (a.getRule().length() > 0
|
+ (a.toString().length() > 0
|
||||||
? a.getRule().substring(0, Math.min(20, a.getRule().length()) - 1)
|
? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
|
||||||
: a.getClass().getSimpleName())
|
: a.getClass().getSimpleName())
|
||||||
+ "..."
|
+ "..."
|
||||||
))
|
))
|
||||||
|
@ -1242,6 +1245,14 @@ public class TestPlayer implements Player {
|
||||||
mustAttackByAction = true;
|
mustAttackByAction = true;
|
||||||
String command = action.getAction();
|
String command = action.getAction();
|
||||||
command = command.substring(command.indexOf("attack:") + 7);
|
command = command.substring(command.indexOf("attack:") + 7);
|
||||||
|
|
||||||
|
// skip attack
|
||||||
|
if (command.startsWith(ATTACK_SKIP)) {
|
||||||
|
it.remove();
|
||||||
|
madeAttackByAction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
String[] groups = command.split("\\$");
|
String[] groups = command.split("\\$");
|
||||||
for (int i = 1; i < groups.length; i++) {
|
for (int i = 1; i < groups.length; i++) {
|
||||||
String group = groups[i];
|
String group = groups[i];
|
||||||
|
@ -1291,6 +1302,11 @@ public class TestPlayer implements Player {
|
||||||
if (mustAttackByAction && !madeAttackByAction) {
|
if (mustAttackByAction && !madeAttackByAction) {
|
||||||
this.chooseStrictModeFailed(game, "select attackers must use attack command but don't");
|
this.chooseStrictModeFailed(game, "select attackers must use attack command but don't");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AI play if no actions available
|
||||||
|
if (!mustAttackByAction && this.AIPlayer) {
|
||||||
|
this.computerPlayer.selectAttackers(game, attackingPlayerId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1306,10 +1322,19 @@ public class TestPlayer implements Player {
|
||||||
UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next();
|
UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next();
|
||||||
// Map of Blocker reference -> list of creatures blocked
|
// Map of Blocker reference -> list of creatures blocked
|
||||||
Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature = new HashMap<>();
|
Map<MageObjectReference, List<MageObjectReference>> blockedCreaturesByCreature = new HashMap<>();
|
||||||
|
boolean mustBlockByAction = false;
|
||||||
for (PlayerAction action : tempActions) {
|
for (PlayerAction action : tempActions) {
|
||||||
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) {
|
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) {
|
||||||
|
mustBlockByAction = true;
|
||||||
String command = action.getAction();
|
String command = action.getAction();
|
||||||
command = command.substring(command.indexOf("block:") + 6);
|
command = command.substring(command.indexOf("block:") + 6);
|
||||||
|
|
||||||
|
// skip block
|
||||||
|
if (command.startsWith(BLOCK_SKIP)) {
|
||||||
|
actions.remove(action);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
String[] groups = command.split("\\$");
|
String[] groups = command.split("\\$");
|
||||||
String blockerName = groups[0];
|
String blockerName = groups[0];
|
||||||
String attackerName = groups[1];
|
String attackerName = groups[1];
|
||||||
|
@ -1324,6 +1349,11 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkMultipleBlockers(game, blockedCreaturesByCreature);
|
checkMultipleBlockers(game, blockedCreaturesByCreature);
|
||||||
|
|
||||||
|
// AI play if no actions available
|
||||||
|
if (!mustBlockByAction && this.AIPlayer) {
|
||||||
|
this.computerPlayer.selectBlockers(game, defendingPlayerId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a creature can block at least one more creature
|
// Checks if a creature can block at least one more creature
|
||||||
|
|
|
@ -10,7 +10,7 @@ import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.GameOptions;
|
import mage.game.GameOptions;
|
||||||
import mage.game.TwoPlayerDuel;
|
import mage.game.TwoPlayerDuel;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import mage.player.ai.ComputerPlayer;
|
import mage.player.ai.ComputerPlayer;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.players.PlayerType;
|
import mage.players.PlayerType;
|
||||||
|
@ -36,7 +36,7 @@ public class PlayGameTest extends MageTestBase {
|
||||||
@Ignore
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException {
|
public void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException {
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
|
|
||||||
Player computerA = createPlayer("ComputerA", PlayerType.COMPUTER_MINIMAX_HYBRID);
|
Player computerA = createPlayer("ComputerA", PlayerType.COMPUTER_MINIMAX_HYBRID);
|
||||||
// Player playerA = createPlayer("ComputerA", "Computer - mad");
|
// Player playerA = createPlayer("ComputerA", "Computer - mad");
|
||||||
|
|
|
@ -10,7 +10,7 @@ import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.GameOptions;
|
import mage.game.GameOptions;
|
||||||
import mage.game.TwoPlayerDuel;
|
import mage.game.TwoPlayerDuel;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import mage.player.ai.ComputerPlayer;
|
import mage.player.ai.ComputerPlayer;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
|
@ -42,7 +42,7 @@ public class TestPlayRandomGame extends MageTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException {
|
private void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException {
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
|
|
||||||
Player computerA = createRandomPlayer("ComputerA");
|
Player computerA = createRandomPlayer("ComputerA");
|
||||||
Deck deck = generateRandomDeck();
|
Deck deck = generateRandomDeck();
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package org.mage.test.serverside.base;
|
package org.mage.test.serverside.base;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.CommanderFreeForAll;
|
import mage.game.CommanderFreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public abstract class CardTestCommander3PlayersFFA extends CardTestPlayerAPIImpl {
|
public abstract class CardTestCommander3PlayersFFA extends CardTestPlayerAPIImpl {
|
||||||
|
@ -24,7 +24,7 @@ public abstract class CardTestCommander3PlayersFFA extends CardTestPlayerAPIImpl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new CommanderFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, new VancouverMulligan(0), 40);
|
Game game = new CommanderFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
||||||
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
||||||
playerC = createPlayer(game, playerC, "PlayerC", deckNameC);
|
playerC = createPlayer(game, playerC, "PlayerC", deckNameC);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.CommanderFreeForAll;
|
import mage.game.CommanderFreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -17,7 +17,7 @@ public abstract class CardTestCommander4Players extends CardTestPlayerAPIImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new CommanderFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new CommanderFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
|
|
||||||
package org.mage.test.serverside.base;
|
package org.mage.test.serverside.base;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.CommanderDuel;
|
import mage.game.CommanderDuel;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl {
|
public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl {
|
||||||
|
@ -24,7 +23,7 @@ public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, new VancouverMulligan(0), 40);
|
Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
|
||||||
|
|
||||||
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
||||||
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -21,7 +21,7 @@ public abstract class CardTestMultiPlayerBase extends CardTestPlayerAPIImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -5,7 +5,7 @@ import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.FreeForAll;
|
import mage.game.FreeForAll;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -17,7 +17,7 @@ public abstract class CardTestMultiPlayerBaseWithRangeAll extends CardTestPlayer
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, new VancouverMulligan(0), 20);
|
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
|
|
@ -1,33 +1,34 @@
|
||||||
package org.mage.test.serverside.base;
|
package org.mage.test.serverside.base;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.MultiplayerAttackOption;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.TwoPlayerDuel;
|
import mage.game.TwoPlayerDuel;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for testing single cards and effects.
|
* Base class for testing single cards and effects.
|
||||||
*
|
*
|
||||||
* @author ayratn
|
* @author ayratn
|
||||||
*/
|
*/
|
||||||
public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl {
|
public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl {
|
||||||
|
|
||||||
public CardTestPlayerBase() {
|
public CardTestPlayerBase() {
|
||||||
deckNameA = "RB Aggro.dck";
|
deckNameA = "RB Aggro.dck";
|
||||||
deckNameB = "RB Aggro.dck";
|
deckNameB = "RB Aggro.dck";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, new VancouverMulligan(0), 20);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
|
|
||||||
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
|
||||||
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.TwoPlayerDuel;
|
import mage.game.TwoPlayerDuel;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import org.mage.test.player.TestComputerPlayer7;
|
import org.mage.test.player.TestComputerPlayer7;
|
||||||
import org.mage.test.player.TestPlayer;
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
|
||||||
|
@ -21,7 +21,7 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, new VancouverMulligan(0), 20);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||||
|
|
||||||
playerA = createPlayer(game, playerA, "PlayerA");
|
playerA = createPlayer(game, playerA, "PlayerA");
|
||||||
playerB = createPlayer(game, playerB, "PlayerB");
|
playerB = createPlayer(game, playerB, "PlayerB");
|
||||||
|
@ -37,8 +37,4 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl {
|
||||||
}
|
}
|
||||||
return super.createPlayer(name, rangeOfInfluence);
|
return super.createPlayer(name, rangeOfInfluence);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAISkill(int skill) {
|
|
||||||
this.skill = skill;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1527,12 +1527,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString());
|
player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void attackSkip(int turnNum, TestPlayer player) {
|
||||||
|
attack(turnNum, player, TestPlayer.ATTACK_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
public void block(int turnNum, TestPlayer player, String blocker, String attacker) {
|
public void block(int turnNum, TestPlayer player, String blocker, String attacker) {
|
||||||
//Assert.assertNotEquals("", blocker);
|
//Assert.assertNotEquals("", blocker);
|
||||||
//Assert.assertNotEquals("", attacker);
|
//Assert.assertNotEquals("", attacker);
|
||||||
player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + '$' + attacker);
|
player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + '$' + attacker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void blockSkip(int turnNum, TestPlayer player) {
|
||||||
|
block(turnNum, player, TestPlayer.BLOCK_SKIP, "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For use choices set "Yes" or "No" the the choice string. For X values set
|
* For use choices set "Yes" or "No" the the choice string. For X values set
|
||||||
* "X=[xValue]" example: for X=3 set choice string to "X=3".
|
* "X=[xValue]" example: for X=3 set choice string to "X=3".
|
||||||
|
|
|
@ -7,7 +7,7 @@ import mage.constants.PlanarDieRoll;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.TwoPlayerDuel;
|
import mage.game.TwoPlayerDuel;
|
||||||
import mage.game.mulligan.VancouverMulligan;
|
import mage.game.mulligan.MulliganType;
|
||||||
import mage.player.human.HumanPlayer;
|
import mage.player.human.HumanPlayer;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
|
@ -93,7 +93,7 @@ public class RandomTest {
|
||||||
String dest = "f:/test/xmage/";
|
String dest = "f:/test/xmage/";
|
||||||
//RandomUtil.setSeed(123);
|
//RandomUtil.setSeed(123);
|
||||||
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 50);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 50);
|
||||||
|
|
||||||
int height = 512;
|
int height = 512;
|
||||||
int weight = 512;
|
int weight = 512;
|
||||||
|
@ -116,7 +116,7 @@ public class RandomTest {
|
||||||
String dest = "f:/test/xmage/";
|
String dest = "f:/test/xmage/";
|
||||||
//RandomUtil.setSeed(123);
|
//RandomUtil.setSeed(123);
|
||||||
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 50);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 50);
|
||||||
|
|
||||||
int height = 512;
|
int height = 512;
|
||||||
int weight = 512;
|
int weight = 512;
|
||||||
|
@ -141,7 +141,7 @@ public class RandomTest {
|
||||||
String dest = "f:/test/xmage/";
|
String dest = "f:/test/xmage/";
|
||||||
//RandomUtil.setSeed(123);
|
//RandomUtil.setSeed(123);
|
||||||
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
Player player = new HumanPlayer("random", RangeOfInfluence.ALL, 1);
|
||||||
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, new VancouverMulligan(0), 50);
|
Game game = new TwoPlayerDuel(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 50);
|
||||||
Deck deck = DeckTestUtils.buildRandomDeck("WGUBR", false, "GRN");
|
Deck deck = DeckTestUtils.buildRandomDeck("WGUBR", false, "GRN");
|
||||||
player.getLibrary().addAll(deck.getCards(), game);
|
player.getLibrary().addAll(deck.getCards(), game);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package mage.abilities.common;
|
||||||
|
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.SpellAbilityType;
|
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,16 +9,20 @@ import mage.constants.Zone;
|
||||||
*/
|
*/
|
||||||
public class CastCommanderAbility extends SpellAbility {
|
public class CastCommanderAbility extends SpellAbility {
|
||||||
|
|
||||||
public CastCommanderAbility(Card card) {
|
private String ruleText;
|
||||||
super(card.getSpellAbility());
|
|
||||||
|
public CastCommanderAbility(Card card, SpellAbility spellTemplate) {
|
||||||
|
super(spellTemplate);
|
||||||
this.newId();
|
this.newId();
|
||||||
this.setCardName(card.getName());
|
this.setCardName(spellTemplate.getCardName());
|
||||||
zone = Zone.COMMAND;
|
this.zone = Zone.COMMAND;
|
||||||
spellAbilityType = SpellAbilityType.BASE;
|
this.spellAbilityType = spellTemplate.getSpellAbilityType();
|
||||||
|
this.ruleText = spellTemplate.getRule(); // need to support custom rule texts like OverloadAbility
|
||||||
}
|
}
|
||||||
|
|
||||||
public CastCommanderAbility(final CastCommanderAbility ability) {
|
public CastCommanderAbility(final CastCommanderAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
|
this.ruleText = ability.ruleText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,4 +30,9 @@ public class CastCommanderAbility extends SpellAbility {
|
||||||
return new CastCommanderAbility(this);
|
return new CastCommanderAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule() {
|
||||||
|
return ruleText;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,20 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Plopman
|
* @author Plopman, JayDi85
|
||||||
*/
|
*/
|
||||||
//20130711
|
// 2019-07-12
|
||||||
/*
|
/*
|
||||||
* 903.11. If a commander would be put into its owner's graveyard from anywhere, that player may put it into the command zone instead.
|
903.9. If a commander would be exiled from anywhere or put into its owner’s hand, graveyard, or library from anywhere,
|
||||||
* 903.12. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead.
|
its owner may put it into the command zone instead. This replacement effect may apply more than once to the same event.
|
||||||
* 903.9. If a commander would be exiled from anywhere or put into its owner's hand, graveyard, or
|
This is an exception to rule 614.5.
|
||||||
library from anywhere, its owner may put it into the command zone instead. This replacement effect
|
903.9a If a commander is a melded permanent and its owner chooses to put it into the command zone this way,
|
||||||
may apply more than once to the same event. This is an exception to rule 614.5.
|
that permanent and the card representing it that isn’t a commander are put into the appropriate zone, and the card
|
||||||
|
that represents it and is a commander is put into the command zone.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Oathbreaker mode: If your Oathbreaker changes zones, you may return it to the Command Zone. The Signature Spell must return to the Command Zone.
|
// Oathbreaker mode: If your Oathbreaker changes zones, you may return it to the Command Zone. The Signature Spell must return to the Command Zone.
|
||||||
|
@ -32,8 +34,8 @@ may apply more than once to the same event. This is an exception to rule 614.5.
|
||||||
public class CommanderReplacementEffect extends ReplacementEffectImpl {
|
public class CommanderReplacementEffect extends ReplacementEffectImpl {
|
||||||
|
|
||||||
private final UUID commanderId;
|
private final UUID commanderId;
|
||||||
private final boolean alsoHand;
|
private final boolean alsoHand; // return from hand to command zone
|
||||||
private final boolean alsoLibrary;
|
private final boolean alsoLibrary; // return from library to command zone
|
||||||
private final boolean forceToMove;
|
private final boolean forceToMove;
|
||||||
private final String commanderTypeName;
|
private final String commanderTypeName;
|
||||||
|
|
||||||
|
@ -87,56 +89,56 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
switch (((ZoneChangeEvent) event).getToZone()) {
|
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||||
case HAND:
|
|
||||||
if (!alsoHand && ((ZoneChangeEvent) event).getToZone() == Zone.HAND) {
|
if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) {
|
||||||
return false;
|
//System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (zEvent.getToZone().equals(Zone.HAND) && !alsoHand) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (zEvent.getToZone().equals(Zone.LIBRARY) && !alsoLibrary) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return to command zone
|
||||||
|
switch (zEvent.getToZone()) {
|
||||||
case LIBRARY:
|
case LIBRARY:
|
||||||
if (!alsoLibrary && ((ZoneChangeEvent) event).getToZone() == Zone.LIBRARY) {
|
case HAND:
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case GRAVEYARD:
|
case GRAVEYARD:
|
||||||
case EXILED:
|
case EXILED:
|
||||||
if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK) {
|
if (commanderId.equals(zEvent.getTargetId())) {
|
||||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
|
||||||
if (spell != null && commanderId.equals(spell.getSourceId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (commanderId.equals(event.getTargetId())) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STACK:
|
|
||||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
|
||||||
if (spell != null) {
|
|
||||||
if (commanderId.equals(spell.getSourceId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||||
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
|
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
String originToZone = zEvent.getToZone().toString().toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
if (!game.isSimulation()) {
|
||||||
|
//System.out.println("replace " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
|
||||||
|
Permanent permanent = zEvent.getTarget();
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
Player player = game.getPlayer(permanent.getOwnerId());
|
Player player = game.getPlayer(permanent.getOwnerId());
|
||||||
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone?", source, game))) {
|
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone instead " + originToZone + "?", source, game))) {
|
||||||
((ZoneChangeEvent) event).setToZone(Zone.COMMAND);
|
zEvent.setToZone(Zone.COMMAND);
|
||||||
if (!game.isSimulation()) {
|
if (!game.isSimulation()) {
|
||||||
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone");
|
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone instead " + originToZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Card card = null;
|
Card card = null;
|
||||||
if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK) {
|
if (zEvent.getFromZone() == Zone.STACK) {
|
||||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||||
if (spell != null) {
|
if (spell != null) {
|
||||||
card = game.getCard(spell.getSourceId());
|
card = game.getCard(spell.getSourceId());
|
||||||
|
@ -147,10 +149,10 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
|
||||||
}
|
}
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
Player player = game.getPlayer(card.getOwnerId());
|
Player player = game.getPlayer(card.getOwnerId());
|
||||||
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone?", source, game))) {
|
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone instead " + originToZone + "?", source, game))) {
|
||||||
((ZoneChangeEvent) event).setToZone(Zone.COMMAND);
|
((ZoneChangeEvent) event).setToZone(Zone.COMMAND);
|
||||||
if (!game.isSimulation()) {
|
if (!game.isSimulation()) {
|
||||||
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone");
|
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone instead " + originToZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ import java.util.UUID;
|
||||||
command zone costs an additional {2} for each previous time the player casting it has cast it from
|
command zone costs an additional {2} for each previous time the player casting it has cast it from
|
||||||
the command zone that game. This additional cost is informally known as the “commander tax.”
|
the command zone that game. This additional cost is informally known as the “commander tax.”
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// cast from hand like Remand do not increase commander tax
|
||||||
|
|
||||||
public class CommanderCostModification extends CostModificationEffectImpl {
|
public class CommanderCostModification extends CostModificationEffectImpl {
|
||||||
|
|
||||||
private final UUID commanderId;
|
private final UUID commanderId;
|
||||||
|
|
|
@ -5,7 +5,6 @@ package mage.constants;
|
||||||
* @author North
|
* @author North
|
||||||
*/
|
*/
|
||||||
public enum AsThoughEffectType {
|
public enum AsThoughEffectType {
|
||||||
|
|
||||||
ATTACK,
|
ATTACK,
|
||||||
ATTACK_AS_HASTE,
|
ATTACK_AS_HASTE,
|
||||||
ACTIVATE_HASTE,
|
ACTIVATE_HASTE,
|
||||||
|
@ -20,7 +19,7 @@ public enum AsThoughEffectType {
|
||||||
BLOCK_FORESTWALK,
|
BLOCK_FORESTWALK,
|
||||||
DAMAGE_NOT_BLOCKED,
|
DAMAGE_NOT_BLOCKED,
|
||||||
BE_BLOCKED,
|
BE_BLOCKED,
|
||||||
PLAY_FROM_NOT_OWN_HAND_ZONE,
|
PLAY_FROM_NOT_OWN_HAND_ZONE, // do not use dialogs in "applies" method for that type of effect (it calls multiple times)
|
||||||
CAST_AS_INSTANT,
|
CAST_AS_INSTANT,
|
||||||
ACTIVATE_AS_INSTANT,
|
ACTIVATE_AS_INSTANT,
|
||||||
DAMAGE,
|
DAMAGE,
|
||||||
|
|
|
@ -23,8 +23,11 @@ public abstract class GameCommanderImpl extends GameImpl {
|
||||||
|
|
||||||
// private final Map<UUID, Cards> mulliganedCards = new HashMap<>();
|
// private final Map<UUID, Cards> mulliganedCards = new HashMap<>();
|
||||||
protected boolean checkCommanderDamage = true;
|
protected boolean checkCommanderDamage = true;
|
||||||
protected boolean alsoHand; // replace commander going to hand
|
|
||||||
protected boolean alsoLibrary; // replace commander going to library
|
// old commander's versions (before 2017) restrict return from hand or library to command zone
|
||||||
|
protected boolean alsoHand = true; // replace commander going to hand
|
||||||
|
protected boolean alsoLibrary = true; // replace commander going to library
|
||||||
|
|
||||||
protected boolean startingPlayerSkipsDraw = true;
|
protected boolean startingPlayerSkipsDraw = true;
|
||||||
|
|
||||||
public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import mage.abilities.text.TextPart;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.FrameStyle;
|
import mage.cards.FrameStyle;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SpellAbilityType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.SuperType;
|
import mage.constants.SuperType;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
@ -19,7 +20,6 @@ import mage.game.events.ZoneChangeEvent;
|
||||||
import mage.util.GameLog;
|
import mage.util.GameLog;
|
||||||
import mage.util.SubTypeList;
|
import mage.util.SubTypeList;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -36,7 +36,17 @@ public class Commander implements CommandObject {
|
||||||
|
|
||||||
// replace spell ability by commander cast spell (to cast from command zone)
|
// replace spell ability by commander cast spell (to cast from command zone)
|
||||||
if (card.getSpellAbility() != null) {
|
if (card.getSpellAbility() != null) {
|
||||||
abilities.add(new CastCommanderAbility(card));
|
abilities.add(new CastCommanderAbility(card, card.getSpellAbility()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace alternative spell abilities by commander cast spell (to cast from command zone)
|
||||||
|
for (Ability ability : card.getAbilities()) {
|
||||||
|
if (ability instanceof SpellAbility) {
|
||||||
|
SpellAbility spellAbility = (SpellAbility) ability;
|
||||||
|
if (spellAbility.getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
|
||||||
|
abilities.add(new CastCommanderAbility(card, spellAbility));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace play land with commander play land (to play from command zone)
|
// replace play land with commander play land (to play from command zone)
|
||||||
|
|
|
@ -91,6 +91,7 @@ public class LondonMulligan extends Mulligan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openingHandSizes.put(playerId, openingHandSizes.get(playerId) - deduction);
|
openingHandSizes.put(playerId, openingHandSizes.get(playerId) - deduction);
|
||||||
|
int newHandSize = openingHandSizes.get(player.getId());
|
||||||
if (deduction == 0) {
|
if (deduction == 0) {
|
||||||
game.fireInformEvent(new StringBuilder(player.getLogName())
|
game.fireInformEvent(new StringBuilder(player.getLogName())
|
||||||
.append(" mulligans for free.")
|
.append(" mulligans for free.")
|
||||||
|
@ -99,14 +100,13 @@ public class LondonMulligan extends Mulligan {
|
||||||
game.fireInformEvent(new StringBuilder(player.getLogName())
|
game.fireInformEvent(new StringBuilder(player.getLogName())
|
||||||
.append(" mulligans")
|
.append(" mulligans")
|
||||||
.append(" down to ")
|
.append(" down to ")
|
||||||
.append((numCards - deduction))
|
.append(newHandSize)
|
||||||
.append(numCards - deduction == 1 ? " card" : " cards").toString());
|
.append(newHandSize == 1 ? " card" : " cards").toString());
|
||||||
}
|
}
|
||||||
player.drawCards(numCards, game);
|
player.drawCards(numCards, game);
|
||||||
|
|
||||||
int handSize = openingHandSizes.get(player.getId());
|
if (player.getHand().size() > newHandSize) {
|
||||||
if (player.getHand().size() > handSize) {
|
int cardsToDiscard = player.getHand().size() - newHandSize;
|
||||||
int cardsToDiscard = player.getHand().size() - handSize;
|
|
||||||
Cards cards = new CardsImpl();
|
Cards cards = new CardsImpl();
|
||||||
cards.addAll(player.getHand());
|
cards.addAll(player.getHand());
|
||||||
TargetCard target = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND,
|
TargetCard target = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND,
|
||||||
|
@ -118,7 +118,8 @@ public class LondonMulligan extends Mulligan {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endMulligan(Game game, UUID playerId) {}
|
public void endMulligan(Game game, UUID playerId) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LondonMulligan copy() {
|
public LondonMulligan copy() {
|
||||||
|
|
|
@ -22,11 +22,11 @@ public enum MulliganType {
|
||||||
return new ParisMulligan(freeMulligans);
|
return new ParisMulligan(freeMulligans);
|
||||||
case CANADIAN_HIGHLANDER:
|
case CANADIAN_HIGHLANDER:
|
||||||
return new CanadianHighlanderMulligan(freeMulligans);
|
return new CanadianHighlanderMulligan(freeMulligans);
|
||||||
case LONDON:
|
|
||||||
return new LondonMulligan(freeMulligans);
|
|
||||||
default:
|
|
||||||
case VANCOUVER:
|
case VANCOUVER:
|
||||||
return new VancouverMulligan(freeMulligans);
|
return new VancouverMulligan(freeMulligans);
|
||||||
|
default:
|
||||||
|
case LONDON:
|
||||||
|
return new LondonMulligan(freeMulligans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ public enum MulliganType {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public MulliganType orDefault(MulliganType defaultMulligan) {
|
public MulliganType orDefault(MulliganType defaultMulligan) {
|
||||||
if (this == GAME_DEFAULT) {
|
if (this == GAME_DEFAULT) {
|
||||||
return defaultMulligan;
|
return defaultMulligan;
|
||||||
|
|
|
@ -1189,11 +1189,17 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
//20091005 - 305.1
|
//20091005 - 305.1
|
||||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
|
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
|
||||||
// int bookmark = game.bookmarkState();
|
// int bookmark = game.bookmarkState();
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
|
// land events must return original zone (uses for commander watcher)
|
||||||
|
Zone cardZoneBefore = game.getState().getZone(card.getId());
|
||||||
|
GameEvent landEventBefore = GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
|
||||||
|
landEventBefore.setZone(cardZoneBefore);
|
||||||
|
game.fireEvent(landEventBefore);
|
||||||
|
|
||||||
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
|
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
|
||||||
landsPlayed++;
|
landsPlayed++;
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
|
GameEvent landEventAfter = GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
|
||||||
|
landEventAfter.setZone(cardZoneBefore);
|
||||||
|
game.fireEvent(landEventAfter);
|
||||||
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
|
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
|
||||||
// game.removeBookmark(bookmark);
|
// game.removeBookmark(bookmark);
|
||||||
resetStoredBookmark(game); // prevent undo after playing a land
|
resetStoredBookmark(game); // prevent undo after playing a land
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package mage.watchers.common;
|
package mage.watchers.common;
|
||||||
|
|
||||||
import mage.constants.WatcherScope;
|
import mage.constants.WatcherScope;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.game.events.GameEvent.EventType;
|
||||||
|
@ -12,7 +13,8 @@ import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcs commanders play count (spell or land)
|
* Calcs commanders play count only from command zone (spell or land)
|
||||||
|
* Cards like Remand can put command to hand and cast it without commander tax increase
|
||||||
*
|
*
|
||||||
* @author JayDi85
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
|
@ -49,7 +51,7 @@ public class CommanderPlaysCountWatcher extends Watcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCommanderObject) {
|
if (isCommanderObject && event.getZone() == Zone.COMMAND) {
|
||||||
int count = playsCount.getOrDefault(possibleCommanderId, 0);
|
int count = playsCount.getOrDefault(possibleCommanderId, 0);
|
||||||
count++;
|
count++;
|
||||||
playsCount.put(possibleCommanderId, count);
|
playsCount.put(possibleCommanderId, count);
|
||||||
|
|
Loading…
Reference in a new issue