" to get the named permanent with
* that index on the battlefield.
- *
+ *
* Permanents are zero indexed in the order they entered the battlefield for
* each controller:
- *
+ *
* findPermanent(new AttackingCreatureFilter(), "Human", ,
* ) Will find the first "Human" creature that entered the battlefield
* under this controller and is attacking.
- *
+ *
* findPermanent(new FilterControllerPermanent(), "Fabled Hero:3",
* , ) Will find the 4th permanent named "Fabled Hero"
* that entered the battlefield under this controller
- *
+ *
* An exception will be thrown if no permanents match the criteria or the
* index is larger than the number of permanents found with that name.
- *
+ *
* failOnNotFound boolean controls if this function returns null for a
* permanent not found on the battlefield. Currently used only as a
* workaround for attackers in selectAttackers() being able to attack
@@ -329,7 +340,7 @@ public class TestPlayer implements Player {
}
UUID modeId = ability.getModes().getModeId(modeNr);
selectedMode = ability.getModes().get(modeId);
- if (modeId != ability.getModes().getMode().getId()) {
+ if (!Objects.equals(modeId, ability.getModes().getMode().getId())) {
ability.getModes().setActiveMode(modeId);
index = 0; // reset target index if mode changes
}
@@ -431,7 +442,7 @@ public class TestPlayer implements Player {
if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
break;
}
- for (Ability ability : computerPlayer.getPlayable(game, true)) {
+ for (Ability ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
if (ability.toString().startsWith(groups[0])) {
int bookmark = game.bookmarkState();
Ability newAbility = ability.copy();
@@ -445,7 +456,7 @@ public class TestPlayer implements Player {
} else {
game.restoreState(bookmark, ability.getRule());
}
-
+ groupsForTargetHandling = null;
}
}
} else if (action.getAction().startsWith("manaActivate:")) {
@@ -526,6 +537,60 @@ public class TestPlayer implements Player {
actions.remove(action);
}
}
+ } else if (action.getAction().startsWith("check:")) {
+ String command = action.getAction();
+ command = command.substring(command.indexOf("check:") + 6);
+
+ String[] params = command.split("@");
+ boolean checkProccessed = false;
+ if (params.length > 0) {
+
+ // check PT: card name, P, T
+ if (params[0].equals(CHECK_COMMAND_PT) && params.length == 4) {
+ assertPT(action, game, computerPlayer, params[1], Integer.parseInt(params[2]), Integer.parseInt(params[3]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+
+ // check ability: card name, ability class, must have
+ if (params[0].equals(CHECK_COMMAND_ABILITY) && params.length == 4) {
+ assertAbility(action, game, computerPlayer, params[1], params[2], Boolean.parseBoolean(params[3]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+
+ // check battlefield count: card name, count
+ if (params[0].equals(CHECK_COMMAND_PERMANENT_COUNT) && params.length == 3) {
+ assertPermanentCount(action, game, computerPlayer, params[1], Integer.parseInt(params[2]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+
+ // check hand count: count
+ if (params[0].equals(CHECK_COMMAND_HAND_COUNT) && params.length == 2) {
+ assertHandCount(action, game, computerPlayer, Integer.parseInt(params[1]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+
+ // check color: card name, colors, must have
+ if (params[0].equals(CHECK_COMMAND_COLOR) && params.length == 4) {
+ assertColor(action, game, computerPlayer, params[1], params[2], Boolean.parseBoolean(params[3]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+
+ // check subtype: card name, subtype, must have
+ if (params[0].equals(CHECK_COMMAND_SUBTYPE) && params.length == 4) {
+ assertSubType(action, game, computerPlayer, params[1], SubType.fromString(params[2]), Boolean.parseBoolean(params[3]));
+ actions.remove(action);
+ checkProccessed = true;
+ }
+ }
+
+ if (!checkProccessed) {
+ Assert.fail("Unknow check command or params: " + command);
+ }
}
}
}
@@ -546,8 +611,105 @@ public class TestPlayer implements Player {
return false;
}
+ private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) {
+ Permanent founded = null;
+ for (Permanent perm : game.getBattlefield().getAllPermanents()) {
+ if (perm.getName().equals(cardName) && perm.getControllerId().equals(player.getId())) {
+ return perm;
+ }
+ }
+ Assert.assertNotNull(action.getActionName() + " - can''t find permanent to check PT: " + cardName, founded);
+ return null;
+ }
+
+ private void assertPT(PlayerAction action, Game game, Player player, String permanentName, int Power, int Toughness) {
+ Permanent perm = findPermanentWithAssert(action, game, player, permanentName);
+
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " have wrong power: " + perm.getPower().getValue() + " <> " + Power,
+ Power, perm.getPower().getValue());
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " have wrong toughness: " + perm.getToughness().getValue() + " <> " + Toughness,
+ Toughness, perm.getToughness().getValue());
+ }
+
+ private void assertAbility(PlayerAction action, Game game, Player player, String permanentName, String abilityClass, boolean mustHave) {
+ Permanent perm = findPermanentWithAssert(action, game, player, permanentName);
+
+ boolean founded = false;
+ for (Ability ability : perm.getAbilities(game)) {
+ if (ability.getClass().getName().equals(abilityClass)) {
+ founded = true;
+ break;
+ }
+ }
+
+ if (mustHave) {
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have ability " + abilityClass, true, founded);
+ } else {
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have not ability " + abilityClass, false, founded);
+ }
+ }
+
+ private void assertPermanentCount(PlayerAction action, Game game, Player player, String permanentName, int count) {
+ int foundedCount = 0;
+ for (Permanent perm : game.getBattlefield().getAllPermanents()) {
+ if (perm.getName().equals(permanentName) && perm.getControllerId().equals(player.getId())) {
+ foundedCount++;
+ }
+ }
+
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must exists in " + count + " instances", count, foundedCount);
+ }
+
+ private void assertHandCount(PlayerAction action, Game game, Player player, int count) {
+ Assert.assertEquals(action.getActionName() + " - hand must contain " + count, count, player.getHand().size());
+ }
+
+ private void assertColor(PlayerAction action, Game game, Player player, String permanentName, String colors, boolean mustHave) {
+ Assert.assertNotEquals(action.getActionName() + " - must setup colors", "", colors);
+
+ Permanent card = findPermanentWithAssert(action, game, player, permanentName);
+ ObjectColor cardColor = card.getColor(game);
+ ObjectColor searchColors = new ObjectColor(colors);
+
+ List colorsHave = new ArrayList<>();
+ List colorsDontHave = new ArrayList<>();
+
+ for (ObjectColor searchColor : searchColors.getColors()) {
+ if (cardColor.shares(searchColor)) {
+ colorsHave.add(searchColor);
+ } else {
+ colorsDontHave.add(searchColor);
+ }
+ }
+
+ if (mustHave) {
+ Assert.assertEquals(action.getActionName() + " - must contain colors [" + searchColors.toString() + "] but found only [" + cardColor.toString() + "]", 0, colorsDontHave.size());
+ } else {
+ Assert.assertEquals(action.getActionName() + " - must not contain colors [" + searchColors.toString() + "] but found [" + cardColor.toString() + "]", 0, colorsHave.size());
+ }
+ }
+
+ private void assertSubType(PlayerAction action, Game game, Player player, String permanentName, SubType subType, boolean mustHave) {
+
+ Permanent perm = findPermanentWithAssert(action, game, player, permanentName);
+
+ boolean founded = false;
+ for (SubType st : perm.getSubtype(game)) {
+ if (st.equals(subType)) {
+ founded = true;
+ break;
+ }
+ }
+
+ if (mustHave) {
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have subtype " + subType, true, founded);
+ } else {
+ Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have not subtype " + subType, false, founded);
+ }
+ }
+
/*
- * Iterates through each player on the current turn and asserts if they can attack or block legally this turn
+ * Iterates through each player on the current turn and asserts if they can attack or block legally this turn
*/
private void checkLegalMovesThisTurn(Game game) {
// Each player is given priority before actual turns start for e.g. leylines and pre-game initialisation
@@ -589,7 +751,7 @@ public class TestPlayer implements Player {
// Loop through players and validate can attack/block this turn
UUID defenderId = null;
//List
- for (Iterator it = actions.iterator(); it.hasNext();) {
+ for (Iterator it = actions.iterator(); it.hasNext(); ) {
PlayerAction action = it.next();
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
String command = action.getAction();
@@ -1403,6 +1565,11 @@ public class TestPlayer implements Player {
return computerPlayer.putCardsOnBottomOfLibrary(cards, game, source, anyOrder);
}
+ @Override
+ public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
+ return computerPlayer.putCardOnTopXOfLibrary(card, game, source, xFromTheTop);
+ }
+
@Override
public boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) {
return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder);
@@ -1473,21 +1640,41 @@ public class TestPlayer implements Player {
computerPlayer.shuffleLibrary(source, game);
}
+ @Override
+ public void revealCards(Ability source, Cards cards, Game game) {
+ computerPlayer.revealCards(source, cards, game);
+ }
+
@Override
public void revealCards(String name, Cards cards, Game game) {
computerPlayer.revealCards(name, cards, game);
}
+ @Override
+ public void revealCards(Ability source, String name, Cards cards, Game game) {
+ computerPlayer.revealCards(name, cards, game);
+ }
+
@Override
public void revealCards(String name, Cards cards, Game game, boolean postToLog) {
computerPlayer.revealCards(name, cards, game, postToLog);
}
+ @Override
+ public void revealCards(Ability source, String name, Cards cards, Game game, boolean postToLog) {
+ computerPlayer.revealCards(name, cards, game, postToLog);
+ }
+
@Override
public void lookAtCards(String name, Cards cards, Game game) {
computerPlayer.lookAtCards(name, cards, game);
}
+ @Override
+ public void lookAtCards(Ability source, String name, Cards cards, Game game) {
+ computerPlayer.lookAtCards(source, name, cards, game);
+ }
+
@Override
public void lookAtCards(String name, Card card, Game game) {
computerPlayer.lookAtCards(name, card, game);
@@ -2217,7 +2404,7 @@ public class TestPlayer implements Player {
@Override
public boolean choose(Outcome outcome, Target target,
- UUID sourceId, Game game
+ UUID sourceId, Game game
) {
// needed to call here the TestPlayer because it's overwitten
return choose(outcome, target, sourceId, game, null);
@@ -2225,7 +2412,7 @@ public class TestPlayer implements Player {
@Override
public boolean choose(Outcome outcome, Cards cards,
- TargetCard target, Game game
+ TargetCard target, Game game
) {
if (!choices.isEmpty()) {
for (String choose2 : choices) {
@@ -2257,7 +2444,7 @@ public class TestPlayer implements Player {
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
- Ability source, Game game
+ Ability source, Game game
) {
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
}
@@ -2270,15 +2457,15 @@ public class TestPlayer implements Player {
@Override
public boolean choosePile(Outcome outcome, String message,
- List extends Card> pile1, List extends Card> pile2,
- Game game
+ List extends Card> pile1, List extends Card> pile2,
+ Game game
) {
return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
}
@Override
public boolean playMana(Ability ability, ManaCost unpaid,
- String promptText, Game game
+ String promptText, Game game
) {
groupsForTargetHandling = null;
return computerPlayer.playMana(ability, unpaid, promptText, game);
@@ -2292,15 +2479,15 @@ public class TestPlayer implements Player {
@Override
public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup,
- List blockerOrder, Game game
+ List blockerOrder, Game game
) {
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
}
@Override
public void assignDamage(int damage, List targets,
- String singleTargetName, UUID sourceId,
- Game game
+ String singleTargetName, UUID sourceId,
+ Game game
) {
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
}
@@ -2319,14 +2506,14 @@ public class TestPlayer implements Player {
@Override
public void pickCard(List cards, Deck deck,
- Draft draft
+ Draft draft
) {
computerPlayer.pickCard(cards, deck, draft);
}
@Override
public boolean scry(int value, Ability source,
- Game game
+ Game game
) {
// Don't scry at the start of the game.
if (game.getTurnNum() == 1 && game.getStep() == null) {
@@ -2337,37 +2524,37 @@ public class TestPlayer implements Player {
@Override
public boolean moveCards(Card card, Zone toZone,
- Ability source, Game game
+ Ability source, Game game
) {
return computerPlayer.moveCards(card, toZone, source, game);
}
@Override
public boolean moveCards(Card card, Zone toZone,
- Ability source, Game game,
- boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
+ Ability source, Game game,
+ boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
) {
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@Override
public boolean moveCards(Cards cards, Zone toZone,
- Ability source, Game game
+ Ability source, Game game
) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set cards, Zone toZone,
- Ability source, Game game
+ Ability source, Game game
) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set cards, Zone toZone,
- Ability source, Game game,
- boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
+ Ability source, Game game,
+ boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@@ -2416,4 +2603,21 @@ public class TestPlayer implements Player {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Player obj = (Player) o;
+ if (this.getId() == null || obj.getId() == null) {
+ return false;
+ }
+
+ return this.getId().equals(obj.getId());
+ }
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index d405956f83b..27d02fbefd8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1,6 +1,8 @@
package org.mage.test.serverside.base.impl;
+import mage.MageInt;
import mage.Mana;
+import mage.ObjectColor;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.cards.decks.Deck;
@@ -46,6 +48,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
// Defines the constant if for activate ability is not target but a ability on the stack to define
public static final String NO_TARGET = "NO_TARGET";
+ public static final String CHECK_COMMAND_PT = "PT";
+ public static final String CHECK_COMMAND_ABILITY = "ABILITY";
+ public static final String CHECK_COMMAND_PERMANENT_COUNT = "PERMANENT_COUNT";
+ public static final String CHECK_COMMAND_HAND_COUNT = "HAND_COUNT";
+ public static final String CHECK_COMMAND_COLOR = "COLOR";
+ public static final String CHECK_COMMAND_SUBTYPE = "SUBTYPE";
+
protected GameOptions gameOptions;
protected String deckNameA;
@@ -63,7 +72,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
UNKNOWN
}
- public CardTestPlayerAPIImpl(){
+ public CardTestPlayerAPIImpl() {
// load all cards to db from class list
ArrayList errorsList = new ArrayList<>();
CardScanner.scan(errorsList);
@@ -218,6 +227,38 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
return player;
}
+ private void check(String checkName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) {
+ String res = "check:" + command;
+ for (String param : params) {
+ res += "@" + param;
+ }
+ player.addAction(checkName, turnNum, step, res);
+ }
+
+ public void checkPT(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer power, Integer toughness) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_PT, permanentName, power.toString(), toughness.toString());
+ }
+
+ public void checkAbility(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Class> abilityClass, Boolean mustHave) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_ABILITY, permanentName, abilityClass.getName(), mustHave.toString());
+ }
+
+ public void checkPermanentCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer count) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_PERMANENT_COUNT, permanentName, count.toString());
+ }
+
+ public void checkHandCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer count) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_HAND_COUNT, count.toString());
+ }
+
+ public void checkColor(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, String colors, Boolean mustHave) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_COLOR, permanentName, colors, mustHave.toString());
+ }
+
+ public void checkSubType(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, SubType subType, Boolean mustHave) {
+ check(checkName, turnNum, step, player, CHECK_COMMAND_SUBTYPE, permanentName, subType.toString(), mustHave.toString());
+ }
+
/**
* Removes all cards from player's library from the game. Usually this
* should be used once before initialization to form the library in certain
@@ -516,20 +557,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
}
- public void assertAbility(Player player, String cardName, Ability ability, boolean flag) throws AssertionError {
- assertAbility(player, cardName, ability, flag, 1);
+ public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave) throws AssertionError {
+ assertAbility(player, cardName, ability, mustHave, 1);
}
/**
* @param player
* @param cardName
* @param ability
- * @param flag true if creature should contain ability, false if it should
+ * @param mustHave true if creature should contain ability, false if it should
* NOT contain it instead
* @param count number of permanents with that ability
* @throws AssertionError
*/
- public void assertAbility(Player player, String cardName, Ability ability, boolean flag, int count) throws AssertionError {
+ public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
int foundCount = 0;
Permanent found = null;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
@@ -545,7 +586,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
Assert.assertTrue("There is another number (" + foundCount + ") as defined (" + count + ") of such permanents under player's control, player=" + player.getName()
+ ", cardName=" + cardName, count == foundCount);
- if (flag) {
+ if (mustHave) {
Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName()
+ ", cardName" + cardName, found.getAbilities(currentGame).containsRule(ability));
} else {
@@ -682,9 +723,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
*
* @param cardName Name of the permanent that should be checked.
* @param type A type to test for
- * @param flag true if creature should have type, false if it should not
+ * @param mustHave true if creature should have type, false if it should not
*/
- public void assertType(String cardName, CardType type, boolean flag) throws AssertionError {
+ public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
Permanent found = null;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) {
if (permanent.getName().equals(cardName)) {
@@ -695,7 +736,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found);
- Assert.assertTrue("(Battlefield) card type not found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == flag));
+ Assert.assertTrue("(Battlefield) card type not found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == mustHave));
}
@@ -738,6 +779,42 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
}
+ /**
+ * Assert permanent color
+ *
+ * @param player player to check
+ * @param cardName card name on battlefield from player
+ * @param searchColors colors list with searchable values
+ * @param mustHave must or not must have that colors
+ */
+ public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
+ Assert.assertNotEquals("must setup colors to search", 0, searchColors.getColorCount());
+
+ Permanent card = getPermanent(cardName, player);
+ ObjectColor cardColor = card.getColor(currentGame);
+
+ List colorsHave = new ArrayList<>();
+ List colorsDontHave = new ArrayList<>();
+
+ for (ObjectColor searchColor : searchColors.getColors()) {
+ if (cardColor.shares(searchColor)) {
+ colorsHave.add(searchColor);
+ } else {
+ colorsDontHave.add(searchColor);
+ }
+ }
+
+ if (mustHave) {
+ Assert.assertEquals("must contain colors [" + searchColors.toString() + "] but found only [" + cardColor.toString() + "]", 0, colorsDontHave.size());
+ } else {
+ Assert.assertEquals("must not contain colors [" + searchColors.toString() + "] but found [" + cardColor.toString() + "]", 0, colorsHave.size());
+ }
+ }
+
+ public void assertColor(Player player, String cardName, String searchColors, boolean mustHave) {
+ assertColor(player, cardName, new ObjectColor(searchColors), mustHave);
+ }
+
/**
* Assert whether a permanent is tapped or not
*
@@ -839,26 +916,26 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
- public void assertManaPool(Player player, ManaType color, int amount){
+ public void assertManaPool(Player player, ManaType color, int amount) {
ManaPool manaPool = currentGame.getPlayer(player.getId()).getManaPool();
- switch (color){
+ switch (color) {
case COLORLESS:
- Assert.assertEquals(amount,manaPool.getColorless() + manaPool.getConditionalMana().stream().mapToInt(Mana::getColorless).sum());
+ Assert.assertEquals(amount, manaPool.getColorless() + manaPool.getConditionalMana().stream().mapToInt(Mana::getColorless).sum());
break;
case RED:
- Assert.assertEquals(amount,manaPool.getRed() + manaPool.getConditionalMana().stream().mapToInt(Mana::getRed).sum());
+ Assert.assertEquals(amount, manaPool.getRed() + manaPool.getConditionalMana().stream().mapToInt(Mana::getRed).sum());
break;
case BLUE:
- Assert.assertEquals(amount,manaPool.getBlue() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlue).sum());
+ Assert.assertEquals(amount, manaPool.getBlue() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlue).sum());
break;
case WHITE:
- Assert.assertEquals(amount,manaPool.getWhite() + manaPool.getConditionalMana().stream().mapToInt(Mana::getWhite).sum());
+ Assert.assertEquals(amount, manaPool.getWhite() + manaPool.getConditionalMana().stream().mapToInt(Mana::getWhite).sum());
break;
case GREEN:
- Assert.assertEquals(amount,manaPool.getGreen() + manaPool.getConditionalMana().stream().mapToInt(Mana::getGreen).sum());
+ Assert.assertEquals(amount, manaPool.getGreen() + manaPool.getConditionalMana().stream().mapToInt(Mana::getGreen).sum());
break;
case BLACK:
- Assert.assertEquals(amount,manaPool.getBlack() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlack).sum());
+ Assert.assertEquals(amount, manaPool.getBlack() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlack).sum());
break;
}
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index 354b9a88ac6..e6cdc6f7bcc 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -758,16 +758,31 @@ public class PlayerStub implements Player {
}
+ @Override
+ public void revealCards(Ability source, Cards cards, Game game) {
+
+ }
+
@Override
public void revealCards(String name, Cards cards, Game game) {
}
+ @Override
+ public void revealCards(Ability source, String name, Cards cards, Game game) {
+
+ }
+
@Override
public void revealCards(String name, Cards cards, Game game, boolean postToLog) {
}
+ @Override
+ public void revealCards(Ability source, String name, Cards cards, Game game, boolean postToLog) {
+
+ }
+
@Override
public void lookAtCards(String name, Card card, Game game) {
@@ -778,6 +793,11 @@ public class PlayerStub implements Player {
}
+ @Override
+ public void lookAtCards(Ability source, String name, Cards cards, Game game) {
+
+ }
+
@Override
public Player copy() {
return null;
@@ -888,6 +908,11 @@ public class PlayerStub implements Player {
return false;
}
+ @Override
+ public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
+ return true;
+ }
+
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
return 0;
@@ -1293,4 +1318,22 @@ public class PlayerStub implements Player {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Player obj = (Player) o;
+ if (this.getId() == null || obj.getId() == null) {
+ return false;
+ }
+
+ return this.getId().equals(obj.getId());
+ }
+
}
diff --git a/Mage.Verify/pom.xml b/Mage.Verify/pom.xml
index e5fa977a770..c0cc0aae2b2 100644
--- a/Mage.Verify/pom.xml
+++ b/Mage.Verify/pom.xml
@@ -41,6 +41,17 @@
jar
+
+ org.reflections
+ reflections
+ 0.9.11
+
+
+ org.mage
+ mage-client
+ 1.4.29
+
+
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 79b88f1110e..acc9db29673 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -1,15 +1,25 @@
package mage.verify;
+import javassist.bytecode.SignatureAttribute;
import mage.ObjectColor;
import mage.cards.*;
import mage.cards.basiclands.BasicLand;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SuperType;
+import mage.game.permanent.token.Token;
+import mage.game.permanent.token.TokenImpl;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
+import org.mage.plugins.card.images.CardDownloadData;
+import org.mage.plugins.card.images.DownloadPictures;
+import org.reflections.Reflections;
import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -19,15 +29,27 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+/**
+ * @author JayDi85
+ */
public class VerifyCardDataTest {
// right now this is very noisy, and not useful enough to make any assertions on
private static final boolean CHECK_SOURCE_TOKENS = false;
private static final HashMap> skipCheckLists = new HashMap<>();
- private static void skipListCreate(String listName){ skipCheckLists.put(listName, new LinkedHashSet<>()); }
- private static void skipListAddName(String listName, String name){ skipCheckLists.get(listName).add(name); }
- private static boolean skipListHaveName(String listName, String name){ return skipCheckLists.get(listName).contains(name); }
+
+ private static void skipListCreate(String listName) {
+ skipCheckLists.put(listName, new LinkedHashSet<>());
+ }
+
+ private static void skipListAddName(String listName, String name) {
+ skipCheckLists.get(listName).add(name);
+ }
+
+ private static boolean skipListHaveName(String listName, String name) {
+ return skipCheckLists.get(listName).contains(name);
+ }
static {
// skip lists for checks (example: unstable cards with same name may have different stats)
@@ -100,7 +122,7 @@ public class VerifyCardDataTest {
private void fail(Card card, String category, String message) {
failed++;
- System.out.println("Error: (" + category + ") " + message + " for " + card.getName() + " (" + card.getExpansionSetCode() + ")");
+ System.out.println("Error: (" + category + ") " + message + " for " + card.getName() + " (" + card.getExpansionSetCode() + ")");
}
private int failed = 0;
@@ -122,31 +144,31 @@ public class VerifyCardDataTest {
}
@Test
- public void checkDuplicateCardNumbersInDB(){
+ public void checkDuplicateCardNumbersInDB() {
Collection doubleErrors = new ArrayList<>();
Collection sets = Sets.getInstance().values();
for (ExpansionSet set : sets) {
Map cardsList = new HashMap<>();
- for (ExpansionSet.SetCardInfo checkCard: set.getSetCardInfo()) {
+ for (ExpansionSet.SetCardInfo checkCard : set.getSetCardInfo()) {
String cardNumber = checkCard.getCardNumber();
// ignore double faced
Card realCard = CardImpl.createCard(checkCard.getCardClass(), new CardSetInfo(checkCard.getName(), set.getCode(),
checkCard.getCardNumber(), checkCard.getRarity(), checkCard.getGraphicInfo()));
- if (realCard.isNightCard()){
+ if (realCard.isNightCard()) {
continue;
}
- if (cardsList.containsKey(cardNumber)){
+ if (cardsList.containsKey(cardNumber)) {
ExpansionSet.SetCardInfo prevCard = cardsList.get(cardNumber);
String errorType;
- if (checkCard.getName().equals(prevCard.getName())){
+ if (checkCard.getName().equals(prevCard.getName())) {
errorType = " founded DUPLICATED cards"
+ " set (" + set.getCode() + " - " + set.getName() + ")"
+ " (" + checkCard.getCardNumber() + " - " + checkCard.getName() + ")";
- }else{
+ } else {
errorType = " founded TYPOS in card numbers"
+ " set (" + set.getCode() + " - " + set.getName() + ")"
+ " (" + prevCard.getCardNumber() + " - " + prevCard.getName() + ")"
@@ -157,23 +179,23 @@ public class VerifyCardDataTest {
String error = "Error: " + errorType;
doubleErrors.add(error);
- }else{
+ } else {
cardsList.put(cardNumber, checkCard);
}
}
}
- for (String error: doubleErrors) {
+ for (String error : doubleErrors) {
System.out.println(error);
}
- if (doubleErrors.size() > 0){
+ if (doubleErrors.size() > 0) {
Assert.fail("DB have duplicated card numbers, founded errors: " + doubleErrors.size());
}
}
@Test
- public void checkWrongCardClasses(){
+ public void checkWrongCardClasses() {
Collection errorsList = new ArrayList<>();
Map classesIndex = new HashMap<>();
int totalCards = 0;
@@ -188,7 +210,7 @@ public class VerifyCardDataTest {
String needClass = classesIndex.get(checkCard.getName());
if (!needClass.equals(currentClass)) {
// workaround to star wars set with same card names
- if(!checkCard.getName().equals("Syndicate Enforcer")) {
+ if (!checkCard.getName().equals("Syndicate Enforcer")) {
errorsList.add("Error: founded wrong class in set " + set.getCode() + " - " + checkCard.getName() + " (" + currentClass + " <> " + needClass + ")");
}
}
@@ -198,49 +220,182 @@ public class VerifyCardDataTest {
}
}
- for (String error: errorsList) {
+ for (String error : errorsList) {
System.out.println(error);
}
// unique cards stats
System.out.println("Total unique cards: " + classesIndex.size() + ", total non unique cards (reprints): " + totalCards);
- if (errorsList.size() > 0){
+ if (errorsList.size() > 0) {
Assert.fail("DB have wrong card classes, founded errors: " + errorsList.size());
}
}
@Test
- public void checkMissingSets(){
+ public void checkMissingSets() {
Collection errorsList = new ArrayList<>();
int totalMissingSets = 0;
int totalMissingCards = 0;
Collection sets = Sets.getInstance().values();
- for(Map.Entry refEntry: MtgJson.sets().entrySet()){
+ for (Map.Entry refEntry : MtgJson.sets().entrySet()) {
JsonSet refSet = refEntry.getValue();
// replace codes for aliases
String searchSet = MtgJson.mtgJsonToXMageCodes.getOrDefault(refSet.code, refSet.code);
ExpansionSet mageSet = Sets.findSet(searchSet);
- if(mageSet == null){
+ if (mageSet == null) {
totalMissingSets = totalMissingSets + 1;
totalMissingCards = totalMissingCards + refSet.cards.size();
errorsList.add("Warning: missing set " + refSet.code + " - " + refSet.name + " (cards: " + refSet.cards.size() + ")");
}
}
- if(errorsList.size() > 0){
+ if (errorsList.size() > 0) {
errorsList.add("Warning: total missing sets: " + totalMissingSets + ", with missing cards: " + totalMissingCards);
}
// only warnings
- for (String error: errorsList) {
+ for (String error : errorsList) {
System.out.println(error);
}
}
+ private Object createNewObject(Class> clazz) {
+ try {
+ Constructor> cons = clazz.getConstructor();
+ return cons.newInstance();
+ } catch (InvocationTargetException ex) {
+ Throwable e = ex.getTargetException();
+ e.printStackTrace();
+ return null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private void printMessages(Collection list) {
+ for (String mes : list) {
+ System.out.println(mes);
+ }
+ }
+
+ private String extractShortClass(Class extends TokenImpl> tokenClass) {
+ String origin = tokenClass.getName();
+ if (origin.contains("$")) {
+ // inner classes, example: mage.cards.f.FigureOfDestiny$FigureOfDestinyToken3
+ return origin.replaceAll(".+\\$(.+)", "$1");
+ } else {
+ // public classes, example: mage.game.permanent.token.FigureOfDestinyToken
+ return origin.replaceAll(".+\\.(.+)", "$1");
+ }
+ }
+
+ @Test
+ @Ignore // TODO: enable test after massive token fixes
+ public void checkMissingTokenData() {
+
+ Collection errorsList = new ArrayList<>();
+ Collection warningsList = new ArrayList<>();
+
+ // all tokens must be stores in card-pictures-tok.txt (if not then viewer and image downloader are missing token images)
+ // https://github.com/ronmamo/reflections
+ Reflections reflections = new Reflections("mage.");
+ Set> tokenClassesList = reflections.getSubTypesOf(TokenImpl.class);
+
+ // xmage tokens
+ Set> privateTokens = new HashSet<>();
+ Set> publicTokens = new HashSet<>();
+ for (Class extends TokenImpl> tokenClass : tokenClassesList) {
+ if (Modifier.isPublic(tokenClass.getModifiers())) {
+ publicTokens.add(tokenClass);
+ } else {
+ privateTokens.add(tokenClass);
+ }
+ }
+
+ // tok file's data
+ ArrayList tokFileTokens = DownloadPictures.getTokenCardUrls();
+ LinkedHashMap tokDataClassesIndex = new LinkedHashMap<>();
+ LinkedHashMap tokDataNamesIndex = new LinkedHashMap<>();
+ for (CardDownloadData tokData : tokFileTokens) {
+
+ String searchName;
+ String setsList;
+
+ // by class
+ searchName = tokData.getTokenClassName();
+ setsList = tokDataClassesIndex.getOrDefault(searchName, "");
+ if (!setsList.isEmpty()) {
+ setsList += ",";
+ }
+ setsList += tokData.getSet();
+ tokDataClassesIndex.put(searchName, setsList);
+
+ // by name
+ searchName = tokData.getName();
+ setsList = tokDataNamesIndex.getOrDefault(searchName, "");
+ if (!setsList.isEmpty()) {
+ setsList += ",";
+ }
+ setsList += tokData.getSet();
+ tokDataNamesIndex.put(searchName, setsList);
+ }
+
+ // 1. check token name convention
+ for (Class extends TokenImpl> tokenClass : tokenClassesList) {
+ if (!tokenClass.getName().endsWith("Token")) {
+ String className = extractShortClass(tokenClass);
+ warningsList.add("warning, token class must ends with Token: " + className + " from " + tokenClass.getName());
+ }
+ }
+
+ // 2. check store file for public
+ for (Class extends TokenImpl> tokenClass : publicTokens) {
+ String fullClass = tokenClass.getName();
+ if (!fullClass.startsWith("mage.game.permanent.token.")) {
+ String className = extractShortClass(tokenClass);
+ errorsList.add("error, public token must stores in mage.game.permanent.token package: " + className + " from " + tokenClass.getName());
+ }
+ }
+
+ // 3. check private tokens (they aren't need at all)
+ for (Class extends TokenImpl> tokenClass : privateTokens) {
+ String className = extractShortClass(tokenClass);
+ errorsList.add("error, no needs in private tokens, replace it with CreatureToken: " + className + " from " + tokenClass.getName());
+ }
+
+ // 4. all public tokens must have tok-data (private tokens uses for innner abilities -- no need images for it)
+ for (Class extends TokenImpl> tokenClass : publicTokens) {
+ String className = extractShortClass(tokenClass);
+ Token token = (Token) createNewObject(tokenClass);
+ //Assert.assertNotNull("Can't create token by default constructor", token);
+ if (token == null) {
+ Assert.fail("Can't create token by default constructor: " + className);
+ }
+
+ if (tokDataNamesIndex.getOrDefault(token.getName(), "").isEmpty()) {
+ errorsList.add("error, can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName());
+ }
+ }
+
+ // TODO: all sets must have full tokens data in tok file (token in every set)
+ // 1. Download scryfall tokens list: https://api.scryfall.com/cards/search?q=t:token
+ // 2. Proccess each token with all prints: read "prints_search_uri" field from token data and go to link like
+ // https://api.scryfall.com/cards/search?order=set&q=%21%E2%80%9CAngel%E2%80%9D&unique=prints
+ // 3. Collect all strings in "set@name"
+ // 4. Proccess tokens data and find missing strings from "set@name" list
+
+ printMessages(warningsList);
+ printMessages(errorsList);
+ if (errorsList.size() > 0) {
+ Assert.fail("Founded token errors: " + errorsList.size());
+ }
+ }
+
private static final Pattern SHORT_JAVA_STRING = Pattern.compile("(?<=\")[A-Z][a-z]+(?=\")");
private Set findSourceTokens(Class c) throws IOException {
@@ -308,7 +463,9 @@ public class VerifyCardDataTest {
}
private void checkColors(Card card, JsonCard ref) {
- if (skipListHaveName("COLOR", card.getName())){ return; }
+ if (skipListHaveName("COLOR", card.getName())) {
+ return;
+ }
Collection expected = ref.colors;
ObjectColor color = card.getColor(null);
@@ -326,14 +483,16 @@ public class VerifyCardDataTest {
}
private void checkSubtypes(Card card, JsonCard ref) {
- if (skipListHaveName("SUBTYPE", card.getName())){ return; }
+ if (skipListHaveName("SUBTYPE", card.getName())) {
+ return;
+ }
Collection expected = ref.subtypes;
// fix names (e.g. Urza’s to Urza's)
if (expected != null && expected.contains("Urza’s")) {
expected = new ArrayList<>(expected);
- for (ListIterator it = ((List) expected).listIterator(); it.hasNext();) {
+ for (ListIterator it = ((List) expected).listIterator(); it.hasNext(); ) {
if (it.next().equals("Urza’s")) {
it.set("Urza's");
}
@@ -346,7 +505,9 @@ public class VerifyCardDataTest {
}
private void checkSupertypes(Card card, JsonCard ref) {
- if (skipListHaveName("SUPERTYPE", card.getName())){ return; }
+ if (skipListHaveName("SUPERTYPE", card.getName())) {
+ return;
+ }
Collection expected = ref.supertypes;
if (!eqSet(card.getSuperType().stream().map(s -> s.toString()).collect(Collectors.toList()), expected)) {
@@ -355,7 +516,9 @@ public class VerifyCardDataTest {
}
private void checkTypes(Card card, JsonCard ref) {
- if (skipListHaveName("TYPE", card.getName())){ return; }
+ if (skipListHaveName("TYPE", card.getName())) {
+ return;
+ }
Collection expected = ref.types;
List type = new ArrayList<>();
@@ -375,7 +538,9 @@ public class VerifyCardDataTest {
}
private void checkPT(Card card, JsonCard ref) {
- if (skipListHaveName("PT", card.getName())){ return; }
+ if (skipListHaveName("PT", card.getName())) {
+ return;
+ }
if (!eqPT(card.getPower().toString(), ref.power) || !eqPT(card.getToughness().toString(), ref.toughness)) {
String pt = card.getPower() + "/" + card.getToughness();
@@ -393,7 +558,9 @@ public class VerifyCardDataTest {
}
private void checkCost(Card card, JsonCard ref) {
- if (skipListHaveName("COST", card.getName())){ return; }
+ if (skipListHaveName("COST", card.getName())) {
+ return;
+ }
String expected = ref.manaCost;
String cost = join(card.getManaCost().getSymbols());
@@ -409,7 +576,9 @@ public class VerifyCardDataTest {
}
private void checkNumbers(Card card, JsonCard ref) {
- if (skipListHaveName("NUMBER", card.getName())){ return; }
+ if (skipListHaveName("NUMBER", card.getName())) {
+ return;
+ }
String expected = ref.number;
String current = card.getCardNumber();
diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java
index 395f52e284d..d173437878b 100644
--- a/Mage/src/main/java/mage/Mana.java
+++ b/Mage/src/main/java/mage/Mana.java
@@ -1083,7 +1083,7 @@ public class Mana implements Comparable, Serializable, Copyable {
&& this.red >= mana.red
&& this.colorless >= mana.colorless
&& (this.generic >= mana.generic
- || this.countColored() >= mana.countColored() + mana.generic);
+ || this.countColored() + this.colorless >= mana.count());
}
diff --git a/Mage/src/main/java/mage/ObjectColor.java b/Mage/src/main/java/mage/ObjectColor.java
index 0c055fa9d65..4d55762e60f 100644
--- a/Mage/src/main/java/mage/ObjectColor.java
+++ b/Mage/src/main/java/mage/ObjectColor.java
@@ -126,6 +126,8 @@ public class ObjectColor implements Serializable, Copyable, Compara
newColor.black = black && other.black;
newColor.red = red && other.red;
newColor.green = green && other.green;
+
+ newColor.gold = gold && other.gold;
return newColor;
}
@@ -157,6 +159,7 @@ public class ObjectColor implements Serializable, Copyable, Compara
List colors = new ArrayList<>();
int firstColor = 5000;
int secondColor = -1;
+
if (this.isWhite()) {
firstColor = 1;
secondColor = 1;
@@ -177,6 +180,7 @@ public class ObjectColor implements Serializable, Copyable, Compara
firstColor = Math.min(firstColor, 5);
secondColor = Math.max(secondColor, 5);
}
+
if (this.isWhite()) {
colors.add(ObjectColor.WHITE);
}
@@ -192,6 +196,7 @@ public class ObjectColor implements Serializable, Copyable, Compara
if (this.isGreen()) {
colors.add(ObjectColor.GREEN);
}
+
if (colors.size() >= 2 && secondColor - firstColor >= 3) {
Collections.swap(colors, 0, 1);
}
@@ -391,7 +396,11 @@ public class ObjectColor implements Serializable, Copyable, Compara
if (test.green != this.green) {
return false;
}
- return test.gold == this.gold;
+ if (test.gold != this.gold) {
+ return false;
+ }
+
+ return true;
}
@Override
@@ -465,10 +474,10 @@ public class ObjectColor implements Serializable, Copyable, Compara
o1 = 4;
} else if (this.isWhite()) {
o1 = 5;
-
} else if (this.isGold()) {
o1 = 6;
}
+
if (o.isMulticolored()) {
o2 = 7;
} else if (o.isColorless()) {
@@ -483,10 +492,10 @@ public class ObjectColor implements Serializable, Copyable, Compara
o2 = 4;
} else if (o.isWhite()) {
o2 = 5;
-
} else if (o.isGold()) {
o2 = 6;
}
+
return o1 - o2;
}
@@ -496,7 +505,12 @@ public class ObjectColor implements Serializable, Copyable, Compara
*
* @return null or
*/
- public ColoredManaSymbol getColoredManaSymbol() {
+ public ColoredManaSymbol getOneColoredManaSymbol() {
+
+ if (isMulticolored()) {
+ throw new IllegalStateException("Founded multicolored object, but it's must call with single mana color.");
+ }
+
if (isBlack()) {
return ColoredManaSymbol.B;
}
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 4f3ae196a25..276d47930b5 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -42,7 +42,7 @@ import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.DynamicManaEffect;
+import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.effects.common.ManaEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.Card;
@@ -157,6 +157,8 @@ public abstract class AbilityImpl implements Ability {
public void newId() {
if (!(this instanceof MageSingleton)) {
this.id = UUID.randomUUID();
+// this.sourceObject = null;
+// this.sourceObjectZoneChangeCounter = -1;
}
getEffects().newId();
}
@@ -1211,7 +1213,7 @@ public abstract class AbilityImpl implements Ability {
@Override
public Permanent getSourcePermanentIfItStillExists(Game game) {
- if (sourceObject == null) {
+ if (sourceObject == null || !sourceObject.getId().equals(getSourceId())) {
setSourceObject(game.getObject(getSourceId()), game);
}
if (sourceObject instanceof Permanent) {
diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java
index 5ef80ab0f1e..073a4596885 100644
--- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java
@@ -250,6 +250,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
return false;
}
+ @Override
public void setMayActivate(TargetController mayActivate) {
this.mayActivate = mayActivate;
}
diff --git a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
index 6dd21c8b275..1f1edfde803 100644
--- a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
+++ b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
@@ -79,6 +79,6 @@ public class ConstellationAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
- return new StringBuilder("Constellation - Whenever {this} or another enchantment enters the battlefield under your control, ").append(super.getRule()).toString();
+ return new StringBuilder("Constellation — Whenever {this} or another enchantment enters the battlefield under your control, ").append(super.getRule()).toString();
}
}
diff --git a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java
index 3bc8a61082b..8ff8641c943 100644
--- a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java
+++ b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java
@@ -86,7 +86,7 @@ public class KinshipAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
- return new StringBuilder("Kinship - At the beginning of your upkeep, ").append(super.getRule()).toString();
+ return new StringBuilder("Kinship — At the beginning of your upkeep, ").append(super.getRule()).toString();
}
}
diff --git a/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java b/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java
index 6471e97e765..701c6dff438 100644
--- a/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java
+++ b/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java
@@ -46,12 +46,12 @@ import mage.constants.Zone;
public class LieutenantAbility extends SimpleStaticAbility {
public LieutenantAbility(ContinuousEffect effect) {
- super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant - As long as you control your commander, {this} gets +2/+2"));
+ super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant — As long as you control your commander, {this} gets +2/+2"));
this.addEffect(new ConditionalContinuousEffect(effect, CommanderInPlayCondition.instance, effect.getText(null)));
}
public LieutenantAbility(Effects effects) {
- super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant - As long as you control your commander, {this} gets +2/+2"));
+ super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant — As long as you control your commander, {this} gets +2/+2"));
for (Effect effect : effects) {
this.addEffect(new ConditionalContinuousEffect((ContinuousEffect) effect, CommanderInPlayCondition.instance, effect.getText(null)));
}
diff --git a/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java b/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java
index 4517b16cd5f..8804fe9b33a 100644
--- a/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java
+++ b/Mage/src/main/java/mage/abilities/abilityword/StriveAbility.java
@@ -68,7 +68,7 @@ public class StriveAbility extends SimpleStaticAbility {
@Override
public String getRule() {
- return new StringBuilder("Strive - {this} costs ").append(striveCost).append(" more to cast for each target beyond the first.").toString();
+ return new StringBuilder("Strive — {this} costs ").append(striveCost).append(" more to cast for each target beyond the first.").toString();
}
}
diff --git a/Mage/src/main/java/mage/abilities/common/LegendarySpellAbility.java b/Mage/src/main/java/mage/abilities/common/LegendarySpellAbility.java
index ca0604d8469..d0ee3f7d0c1 100644
--- a/Mage/src/main/java/mage/abilities/common/LegendarySpellAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/LegendarySpellAbility.java
@@ -1,18 +1,27 @@
package mage.abilities.common;
-import mage.abilities.condition.common.LegendaryCondition;
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SuperType;
import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.mageobject.CardTypePredicate;
+import mage.filter.predicate.mageobject.SupertypePredicate;
+import mage.game.Game;
+import mage.game.events.GameEvent;
/**
- * @author JRHerlehy
- * Created on 4/8/18.
+ * @author JRHerlehy Created on 4/8/18.
*/
public class LegendarySpellAbility extends SimpleStaticAbility {
public LegendarySpellAbility() {
- super(Zone.ALL, new CastOnlyIfConditionIsTrueEffect(LegendaryCondition.instance));
+ super(Zone.ALL, new LegendarySpellAbilityCheckEffect());
this.setRuleAtTheTop(true);
- this.getEffects().get(0).setText("(You may cast a legendary sorcery only if you control a legendary creature or planeswalker.)");
}
private LegendarySpellAbility(final LegendarySpellAbility ability) {
@@ -24,3 +33,46 @@ public class LegendarySpellAbility extends SimpleStaticAbility {
return new LegendarySpellAbility(this);
}
}
+
+class LegendarySpellAbilityCheckEffect extends ContinuousRuleModifyingEffectImpl {
+
+ private static final FilterPermanent filter = new FilterPermanent("legendary creature or planeswalker");
+
+ static {
+ filter.add(
+ Predicates.and(
+ new SupertypePredicate(SuperType.LEGENDARY),
+ Predicates.or(
+ new CardTypePredicate(CardType.CREATURE),
+ new CardTypePredicate(CardType.PLANESWALKER)
+ )
+ )
+ );
+ }
+
+ public LegendarySpellAbilityCheckEffect() {
+ super(Duration.EndOfGame, Outcome.Detriment);
+ staticText = "(You may cast a legendary sorcery only if you control a legendary creature or planeswalker.)";
+ }
+
+ private LegendarySpellAbilityCheckEffect(final LegendarySpellAbilityCheckEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public boolean checksEventType(GameEvent event, Game game) {
+ return event.getType() == GameEvent.EventType.CAST_SPELL;
+ }
+
+ @Override
+ public boolean applies(GameEvent event, Ability source, Game game) {
+ return event.getSourceId().equals(source.getSourceId())
+ && !game.getBattlefield().contains(filter, event.getPlayerId(), 1, game);
+ }
+
+ @Override
+ public LegendarySpellAbilityCheckEffect copy() {
+ return new LegendarySpellAbilityCheckEffect(this);
+ }
+
+}
diff --git a/Mage/src/main/java/mage/abilities/condition/common/LegendaryCondition.java b/Mage/src/main/java/mage/abilities/condition/common/LegendaryCondition.java
deleted file mode 100644
index 1dc607643d7..00000000000
--- a/Mage/src/main/java/mage/abilities/condition/common/LegendaryCondition.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package mage.abilities.condition.common;
-
-import mage.abilities.Ability;
-import mage.abilities.condition.Condition;
-import mage.constants.CardType;
-import mage.constants.SuperType;
-import mage.filter.FilterPermanent;
-import mage.filter.predicate.Predicates;
-import mage.filter.predicate.mageobject.CardTypePredicate;
-import mage.filter.predicate.mageobject.SupertypePredicate;
-import mage.game.Game;
-
-/**
- * @author JRHerlehy
- * Created on 4/7/18.
- */
-public enum LegendaryCondition implements Condition {
-
- instance;
-
- private static final FilterPermanent filter = new FilterPermanent("legendary creature or planeswalker");
-
- static {
- filter.add(
- Predicates.and(
- new SupertypePredicate(SuperType.LEGENDARY),
- Predicates.or(
- new CardTypePredicate(CardType.CREATURE),
- new CardTypePredicate(CardType.PLANESWALKER)
- )
- )
- );
- }
-
-
- @Override
- public boolean apply(Game game, Ability source) {
- return game.getBattlefield().contains(filter, source.getControllerId(), 1, game);
- }
-
-
- @Override
- public String toString() {
- return super.toString();
- }
-}
diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java
index 5f692b1a264..f1ef74cdc04 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromStackCost.java
@@ -63,7 +63,7 @@ public class ExileFromStackCost extends CostImpl {
}
String spellName = spellToExile.getName();
if (spellToExile.isCopy()) {
- game.getStack().remove(spellToExile);
+ game.getStack().remove(spellToExile, game);
} else {
spellToExile.moveToExile(null, "", ability.getSourceId(), game);
}
diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalManaEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalManaEffect.java
index ba692a491d1..085dcafcedd 100644
--- a/Mage/src/main/java/mage/abilities/decorator/ConditionalManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalManaEffect.java
@@ -30,8 +30,8 @@ package mage.abilities.decorator;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
-import mage.abilities.effects.common.BasicManaEffect;
import mage.abilities.effects.common.ManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.choices.ChoiceColor;
import mage.game.Game;
import mage.players.Player;
@@ -73,31 +73,7 @@ public class ConditionalManaEffect extends ManaEffect {
if (controller == null) {
return false;
}
- if (condition.apply(game, source)) {
- effect.setTargetPointer(this.targetPointer);
- } else if (otherwiseEffect != null) {
- otherwiseEffect.setTargetPointer(this.targetPointer);
- }
- Mana mana = getMana(game, source);
- if (mana == null) {
- return false;
- }
- if (mana.getAny() > 0) {
- int amount = mana.getAny();
-
- ChoiceColor choice = new ChoiceColor(true);
- Mana createdMana = null;
- if (controller.choose(outcome, choice, game)) {
- createdMana = choice.getMana(amount);
- }
- if (createdMana == null) {
- return false;
- }
- mana = createdMana;
- // because the mana type is now choosen, fire the event with the mana information
- checkToFirePossibleEvents(mana, game, source);
- }
- controller.getManaPool().addMana(mana, game, source);
+ controller.getManaPool().addMana(getMana(game, source), game, source);
return true;
}
@@ -107,12 +83,25 @@ public class ConditionalManaEffect extends ManaEffect {
}
@Override
- public Mana getMana(Game game, Ability source) {
- Mana mana = null;
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Mana mana = new Mana();
if (condition.apply(game, source)) {
- mana = effect.getMana();
+ mana = effect.getManaTemplate().copy();
} else if (otherwiseEffect != null) {
- mana = otherwiseEffect.getMana();
+ mana = otherwiseEffect.getManaTemplate().copy();
+ }
+ if (mana.getAny() > 0) {
+ int amount = mana.getAny();
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
+ return mana;
+ }
+ ChoiceColor choice = new ChoiceColor(true);
+ if (controller.choose(outcome, choice, game)) {
+ mana.setAny(0);
+ mana.add(choice.getMana(amount));
+ }
+ checkToFirePossibleEvents(mana, game, source);
}
return mana;
}
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsLostLifeCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsLostLifeCount.java
index 131609a7c21..994feb59f83 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsLostLifeCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsLostLifeCount.java
@@ -48,7 +48,7 @@ public class OpponentsLostLifeCount implements DynamicValue {
public int calculate(Game game, UUID controllerId) {
PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get(PlayerLostLifeWatcher.class.getSimpleName());
if (watcher != null) {
- return watcher.getAllOppLifeLost(controllerId);
+ return watcher.getAllOppLifeLost(controllerId, game);
}
return 0;
}
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java
index 6f630c48617..db00aa1ccbe 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SignInversionDynamicValue.java
@@ -8,18 +8,30 @@ import mage.game.Game;
public class SignInversionDynamicValue implements DynamicValue {
private final DynamicValue value;
+ private final boolean canBePositive;
public SignInversionDynamicValue(DynamicValue value) {
+ this(value, true);
+ }
+
+ public SignInversionDynamicValue(DynamicValue value, boolean canBePositive) {
this.value = value.copy();
+ this.canBePositive = canBePositive;
}
SignInversionDynamicValue(final SignInversionDynamicValue dynamicValue) {
this.value = dynamicValue.value.copy();
+ this.canBePositive = dynamicValue.canBePositive;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
- return -1 * value.calculate(game, sourceAbility, effect);
+ int amount = value.calculate(game, sourceAbility, effect);
+ if (amount >= 0 || canBePositive) {
+ return -1 * amount;
+ } else {
+ return 0;
+ }
}
@Override
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index db908e4a289..90306488a3c 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -926,6 +926,14 @@ public class ContinuousEffects implements Serializable {
while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker)
done = true;
layer = filterLayeredEffects(activeLayerEffects, Layer.AbilityAddingRemovingEffects_6);
+
+ // debug
+ /*
+ System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "need apply " + layer.stream()
+ .map((eff) -> {return eff.getClass().getName().replaceAll(".+\\.(.+)", "$1");})
+ .collect(Collectors.joining(", ")));
+ */
+
for (ContinuousEffect effect : layer) {
if (activeLayerEffects.contains(effect) && !appliedEffects.contains(effect.getId())) { // Effect does still exist and was not applied yet
Set dependentTo = effect.isDependentTo(layer);
diff --git a/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java b/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java
index 7979ad8f1b1..03fba3bbe25 100644
--- a/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/RedirectionEffect.java
@@ -27,6 +27,7 @@
*/
package mage.abilities.effects;
+import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.Duration;
import mage.constants.EffectType;
@@ -44,26 +45,34 @@ import mage.target.Target;
*/
public abstract class RedirectionEffect extends ReplacementEffectImpl {
- protected Target redirectTarget;
- protected int amountToRedirect;
- protected boolean oneUsage;
-
- public RedirectionEffect(Duration duration) {
- this(duration, Integer.MAX_VALUE, false);
+ public enum UsageType {
+ ACCORDING_DURATION,
+ ONE_USAGE_ABSOLUTE,
+ ONE_USAGE_AT_THE_SAME_TIME; // all damage dealt at the same time
}
- public RedirectionEffect(Duration duration, int amountToRedirect, boolean oneUsage) {
+ protected Target redirectTarget;
+ protected int amountToRedirect;
+ protected UsageType usageType;
+ protected int applyEffectsCounter;
+
+ public RedirectionEffect(Duration duration) {
+ this(duration, Integer.MAX_VALUE, UsageType.ACCORDING_DURATION);
+ applyEffectsCounter = -1;
+ }
+
+ public RedirectionEffect(Duration duration, int amountToRedirect, UsageType usageType) {
super(duration, Outcome.RedirectDamage);
this.effectType = EffectType.REDIRECTION;
this.amountToRedirect = amountToRedirect;
- this.oneUsage = oneUsage;
+ this.usageType = usageType;
}
public RedirectionEffect(final RedirectionEffect effect) {
super(effect);
this.redirectTarget = effect.redirectTarget;
this.amountToRedirect = effect.amountToRedirect;
- this.oneUsage = effect.oneUsage;
+ this.usageType = effect.usageType;
}
@Override
@@ -79,26 +88,38 @@ public abstract class RedirectionEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+ int damageToRedirect = event.getAmount();
+ if (damageToRedirect < 1) { // if multiple replacement effect apply, the rest damage can be 0, so the effect is not applied/replaced
+ return false;
+ }
String sourceLogName = source != null ? game.getObject(source.getSourceId()).getLogName() + ": " : "";
DamageEvent damageEvent = (DamageEvent) event;
int restDamage = 0;
- int damageToRedirect = event.getAmount();
if (damageEvent.getAmount() > amountToRedirect) {
restDamage = damageEvent.getAmount() - amountToRedirect;
damageToRedirect = amountToRedirect;
}
- if (damageToRedirect > 0 && oneUsage) {
- this.discard();
+ if (damageToRedirect > 0 && usageType != UsageType.ACCORDING_DURATION) {
+ if (UsageType.ONE_USAGE_ABSOLUTE == usageType) {
+ this.discard();
+ }
+ if (applyEffectsCounter > 0) {
+ if (applyEffectsCounter < game.getState().getApplyEffectsCounter()) {
+ this.discard();
+ }
+ } else {
+ applyEffectsCounter = game.getState().getApplyEffectsCounter();
+ }
}
Permanent permanent = game.getPermanent(redirectTarget.getFirstTarget());
if (permanent != null) {
permanent.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects());
- game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + permanent.getLogName());
+ game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage" + getRedirectedFromText(event, game) + " to " + permanent.getLogName());
} else {
Player player = game.getPlayer(redirectTarget.getFirstTarget());
if (player != null) {
player.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects());
- game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + player.getLogName());
+ game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage" + getRedirectedFromText(event, game) + " to " + player.getLogName());
}
}
if (restDamage > 0) {
@@ -108,4 +129,16 @@ public abstract class RedirectionEffect extends ReplacementEffectImpl {
return true;
}
+ private String getRedirectedFromText(GameEvent event, Game game) {
+ Player player = game.getPlayer(event.getTargetId());
+ if (player != null) {
+ return " from " + player.getLogName();
+ }
+ MageObject mageObject = game.getObject(event.getTargetId());
+ if (mageObject != null) {
+ return " from " + mageObject.getLogName();
+ }
+ return "";
+
+ }
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalColorlessManaEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AddConditionalColorlessManaEffect.java
deleted file mode 100644
index 596b447002b..00000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalColorlessManaEffect.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package mage.abilities.effects.common;
-
-import mage.Mana;
-import mage.abilities.Ability;
-import mage.abilities.mana.builder.ConditionalManaBuilder;
-import mage.game.Game;
-import mage.players.Player;
-
-/**
- *
- * @author LevelX2
- */
-public class AddConditionalColorlessManaEffect extends ManaEffect {
-
- private final int amount;
- private final ConditionalManaBuilder manaBuilder;
-
- public AddConditionalColorlessManaEffect(int amount, ConditionalManaBuilder manaBuilder) {
- super();
- this.amount = amount;
- this.manaBuilder = manaBuilder;
-
- staticText = "Add " + String.format(String.format("%%%ds", amount), " ").replace(" ", "{C}")
- + ". " + manaBuilder.getRule();
- }
-
- public AddConditionalColorlessManaEffect(final AddConditionalColorlessManaEffect effect) {
- super(effect);
- this.amount = effect.amount;
- this.manaBuilder = effect.manaBuilder;
- }
-
- @Override
- public AddConditionalColorlessManaEffect copy() {
- return new AddConditionalColorlessManaEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player player = game.getPlayer(source.getControllerId());
- if (player != null) {
- player.getManaPool().addMana(getMana(game, source), game, source);
- return true;
- }
- return false;
- }
-
- @Override
- public Mana getMana(Game game, Ability source) {
- return manaBuilder.setMana(Mana.ColorlessMana(amount), source, game).build();
- }
-
- public Mana getMana() {
- return Mana.ColorlessMana(amount);
- }
-}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorToManaPoolTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorToManaPoolTargetPlayerEffect.java
deleted file mode 100644
index 1f8c6c229be..00000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorToManaPoolTargetPlayerEffect.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package mage.abilities.effects.common;
-
-import java.util.UUID;
-import mage.Mana;
-import mage.abilities.Ability;
-import mage.choices.ChoiceColor;
-import mage.game.Game;
-import mage.players.Player;
-
-/**
- *
- * Created by Galatolol
- */
-public class AddManaOfAnyColorToManaPoolTargetPlayerEffect extends ManaEffect {
-
- public AddManaOfAnyColorToManaPoolTargetPlayerEffect(String textManaPoolOwner) {
- super();
- this.staticText = (textManaPoolOwner.equals("their") ? "that player adds " : "add ") + "one mana of any color" + " to " + textManaPoolOwner + " mana pool";
- }
-
- public AddManaOfAnyColorToManaPoolTargetPlayerEffect(final AddManaOfAnyColorToManaPoolTargetPlayerEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- UUID playerId = (UUID) game.getState().getValue(source.getSourceId() + "_player");
- Player player = game.getPlayer(playerId);
- ChoiceColor choice = new ChoiceColor();
- if (player != null && player.choose(outcome, choice, game)) {
- Mana mana = choice.getMana(1);
- checkToFirePossibleEvents(mana, game, source);
- player.getManaPool().addMana(mana, game, source);
- return true;
- }
- return false;
- }
-
- @Override
- public AddManaOfAnyColorToManaPoolTargetPlayerEffect copy() {
- return new AddManaOfAnyColorToManaPoolTargetPlayerEffect(this);
- }
-
- @Override
- public Mana getMana(Game game, Ability source) {
- return null;
- }
-
-}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java
index f1b8a2238f6..2ec9169c65f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java
@@ -27,6 +27,7 @@
*/
package mage.abilities.effects.common;
+import java.util.Objects;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
@@ -36,8 +37,6 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
-import java.util.Objects;
-
/**
*
* @author jeffwadsworth
@@ -67,6 +66,17 @@ public class CantBeRegeneratedSourceEffect extends ContinuousRuleModifyingEffect
return event.getType() == EventType.REGENERATE;
}
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game); //To change body of generated methods, choose Tools | Templates.
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ }
+ }
+ }
+
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return Objects.equals(source.getSourceId(), event.getTargetId());
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DestroySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DestroySourceEffect.java
index d3bae2ecd35..f3a62ed2319 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DestroySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DestroySourceEffect.java
@@ -63,7 +63,7 @@ public class DestroySourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(source.getSourceId());
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent != null) {
permanent.destroy(source.getSourceId(), game, noRegen);
return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java
index bb3c8496625..e0882ae4466 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java
@@ -71,6 +71,7 @@ public class DoIfCostPaid extends OneShotEffect {
}
message = getCostText() + " and " + effectText + '?';
message = Character.toUpperCase(message.charAt(0)) + message.substring(1);
+ CardUtil.replaceSourceName(message, mageObject.getName());
} else {
message = chooseUseText;
}
@@ -79,6 +80,7 @@ public class DoIfCostPaid extends OneShotEffect {
if (cost.canPay(source, source.getSourceId(), player.getId(), game)
&& executingEffects.size() > 0 && (!optional || player.chooseUse(executingEffects.get(0).getOutcome(), message, source, game))) {
cost.clearPaid();
+ int bookmark = game.bookmarkState();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
for (Effect effect : executingEffects) {
effect.setTargetPointer(this.targetPointer);
@@ -89,13 +91,17 @@ public class DoIfCostPaid extends OneShotEffect {
}
}
player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek
- } else if (!otherwiseEffects.isEmpty()) {
- for (Effect effect : otherwiseEffects) {
- effect.setTargetPointer(this.targetPointer);
- if (effect instanceof OneShotEffect) {
- result &= effect.apply(game, source);
- } else {
- game.addEffect((ContinuousEffect) effect, source);
+ } else {
+ // Paying cost was cancels so try to undo payment so far
+ game.restoreState(bookmark, DoIfCostPaid.class.getName());
+ if (!otherwiseEffects.isEmpty()) {
+ for (Effect effect : otherwiseEffects) {
+ effect.setTargetPointer(this.targetPointer);
+ if (effect instanceof OneShotEffect) {
+ result &= effect.apply(game, source);
+ } else {
+ game.addEffect((ContinuousEffect) effect, source);
+ }
}
}
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java
index a97b997d289..1f78bbbe680 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java
@@ -27,6 +27,7 @@
*/
package mage.abilities.effects.common;
+import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
@@ -50,21 +51,19 @@ public class FightTargetSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent originalPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
- if (originalPermanent != null) {
- Permanent sourcePermanent = game.getPermanent(source.getSourceId());
- // only if target is legal the effect will be applied
- if (source.getTargets().get(0).isLegal(source, game)) {
- Permanent creature1 = game.getPermanent(source.getTargets().get(0).getFirstTarget());
- // 20110930 - 701.10
- if (creature1 != null && sourcePermanent != null) {
- if (creature1.isCreature() && sourcePermanent.isCreature()) {
- return sourcePermanent.fight(creature1, source, game);
- }
+ MageObject sourceObject = source.getSourceObject(game);
+ if (sourceObject != null) {
+ Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
+ Permanent creature1 = game.getPermanent(getTargetPointer().getFirst(game, source));
+ // 20110930 - 701.10
+ if (creature1 != null && sourcePermanent != null) {
+ if (creature1.isCreature() && sourcePermanent.isCreature()) {
+ return sourcePermanent.fight(creature1, source, game);
}
}
- if (!game.isSimulation())
- game.informPlayers(originalPermanent.getLogName() + ": Fighting effect has been fizzled.");
+ if (!game.isSimulation()) {
+ game.informPlayers(sourceObject.getLogName() + ": Fighting effect has been fizzled.");
+ }
}
return false;
}
@@ -83,4 +82,3 @@ public class FightTargetSourceEffect extends OneShotEffect {
}
}
-
diff --git a/Mage/src/main/java/mage/abilities/effects/common/FlipSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FlipSourceEffect.java
index aaa46793726..697a83b2a96 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/FlipSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/FlipSourceEffect.java
@@ -8,11 +8,9 @@ import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
-import mage.game.permanent.token.TokenImpl;
import mage.game.permanent.token.Token;
import mage.players.Player;
-
/**
* @author Loki
*/
@@ -33,14 +31,15 @@ public class FlipSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(source.getSourceId());
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
Player controller = game.getPlayer(source.getControllerId());
if (permanent != null && controller != null) {
if (permanent.flip(game)) {
ContinuousEffect effect = new ConditionalContinuousEffect(new CopyTokenEffect(flipToken), FlippedCondition.instance, "");
game.addEffect(effect, source);
- if (!game.isSimulation())
+ if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(" flips ").append(permanent.getName()).toString());
+ }
return true;
}
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java
index ef9cb95b64d..d27e8f43208 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java
@@ -35,7 +35,6 @@ import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
-import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
@@ -150,6 +149,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop,
boolean reveal, boolean upTo, Zone targetZonePickedCards,
boolean optional) {
+
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
targetZoneLookedCards, putOnTop, reveal, upTo,
targetZonePickedCards, optional, true, true);
@@ -207,16 +207,10 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
}
@Override
- protected void cardLooked(Card card, Game game, Ability source) {
- if (numberToPick.calculate(game, source, this) > 0 && filter.match(card, game)) {
- ++foundCardsToPick;
- }
- }
-
- @Override
- protected void actionWithSelectedCards(Cards cards, Game game, Ability source, String windowName) {
+ protected void actionWithSelectedCards(Cards cards, Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
- if (player != null && foundCardsToPick > 0) {
+ if (player != null && numberToPick.calculate(game, source, this) > 0
+ && cards.count(filter, source.getSourceId(), source.getControllerId(), game) > 0) {
if (!optional || player.chooseUse(Outcome.DrawCard, getMayText(), source, game)) {
FilterCard pickFilter = filter.copy();
pickFilter.setMessage(getPickText());
@@ -231,7 +225,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
player.moveCards(pickedCards.getCards(game), targetPickedCards, source, game);
}
if (revealPickedCards) {
- player.revealCards(windowName, pickedCards, game);
+ player.revealCards(source, pickedCards, game);
}
}
@@ -346,7 +340,14 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
}
sb.append(" order");
} else if (targetZoneLookedCards == Zone.GRAVEYARD) {
- sb.append(" and the other into your graveyard");
+ sb.append(" and the");
+ if (numberOfCards instanceof StaticValue && numberToPick instanceof StaticValue
+ && ((StaticValue) numberToPick).getValue() + 1 == ((StaticValue) numberOfCards).getValue()) {
+ sb.append(" other");
+ } else {
+ sb.append(" rest");
+ }
+ sb.append(" into your graveyard");
}
}
// get text frame from super class and inject action text
diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java
index 93f260874f6..105f7b7151e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryControllerEffect.java
@@ -29,18 +29,15 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
-import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
@@ -105,46 +102,25 @@ public class LookLibraryControllerEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- String windowName = "Reveal";
-
- if (source instanceof SpellAbility) {
- Card sourceCard = game.getCard(source.getSourceId());
- if (sourceCard != null) {
- windowName = sourceCard.getIdName();
- }
- } else {
- Permanent sourcePermanent = game.getPermanent(source.getSourceId());
- if (sourcePermanent != null) {
- windowName = sourcePermanent.getIdName();
- }
- }
-
- Player player = game.getPlayer(source.getControllerId());
- if (player == null) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
return false;
}
// take cards from library and look at them
- boolean topCardRevealed = player.isTopCardRevealed();
- player.setTopCardRevealed(false);
- Cards cards = new CardsImpl();
- int count = Math.min(player.getLibrary().size(), this.numberOfCards.calculate(game, source, this));
- for (int i = 0; i < count; i++) {
- Card card = player.getLibrary().removeFromTop(game);
- if (card != null) {
- cards.add(card);
- this.cardLooked(card, game, source);
- }
- }
- player.lookAtCards(windowName, cards, game);
+ boolean topCardRevealed = controller.isTopCardRevealed();
+ controller.setTopCardRevealed(false);
+ Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, this.numberOfCards.calculate(game, source, this)));
- this.actionWithSelectedCards(cards, game, source, windowName);
+ controller.lookAtCards(source, null, cards, game);
- this.putCardsBack(source, player, cards, game);
+ this.actionWithSelectedCards(cards, game, source);
- player.setTopCardRevealed(topCardRevealed);
+ this.putCardsBack(source, controller, cards, game);
- this.mayShuffle(player, source, game);
+ controller.setTopCardRevealed(topCardRevealed);
+
+ this.mayShuffle(controller, source, game);
return true;
}
@@ -158,10 +134,7 @@ public class LookLibraryControllerEffect extends OneShotEffect {
return this;
}
- protected void cardLooked(Card card, Game game, Ability source) {
- }
-
- protected void actionWithSelectedCards(Cards cards, Game game, Ability source, String windowName) {
+ protected void actionWithSelectedCards(Cards cards, Game game, Ability source) {
}
/**
diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java
index 3081206ac6f..3b10ddb9ba5 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryTopCardTargetPlayerEffect.java
@@ -47,11 +47,13 @@ public class LookLibraryTopCardTargetPlayerEffect extends OneShotEffect {
protected int amount;
protected boolean putToGraveyard;
+ protected boolean mayShuffleAfter; // for Visions
public LookLibraryTopCardTargetPlayerEffect(int amount) {
super(Outcome.Benefit);
this.amount = amount;
this.putToGraveyard = false;
+ this.mayShuffleAfter = false;
setText();
}
@@ -59,6 +61,15 @@ public class LookLibraryTopCardTargetPlayerEffect extends OneShotEffect {
super(Outcome.Benefit);
this.amount = amount;
this.putToGraveyard = putToGraveyard;
+ this.mayShuffleAfter = false;
+ setText();
+ }
+
+ public LookLibraryTopCardTargetPlayerEffect(int amount, boolean putToGraveyard, boolean mayShuffleAfter) {
+ super(Outcome.Benefit);
+ this.amount = amount;
+ this.putToGraveyard = putToGraveyard;
+ this.mayShuffleAfter = mayShuffleAfter;
setText();
}
@@ -70,6 +81,7 @@ public class LookLibraryTopCardTargetPlayerEffect extends OneShotEffect {
super(effect);
amount = effect.amount;
putToGraveyard = effect.putToGraveyard;
+ mayShuffleAfter = effect.mayShuffleAfter;
}
@Override
@@ -95,6 +107,11 @@ public class LookLibraryTopCardTargetPlayerEffect extends OneShotEffect {
}
}
}
+ if (mayShuffleAfter) {
+ if (player.chooseUse(Outcome.Benefit, (player == targetPlayer ? "Shuffle your library?" : "Do you want the chosen player to shuffle his or her library?"), source, game)) {
+ targetPlayer.shuffleLibrary(source, game);
+ }
+ }
return true;
}
return false;
@@ -118,6 +135,9 @@ public class LookLibraryTopCardTargetPlayerEffect extends OneShotEffect {
}
sb.append(" into that player's graveyard");
}
+ if (mayShuffleAfter) {
+ sb.append(". You may then have that player shuffle that library");
+ }
this.staticText = sb.toString();
}
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java
index e489056436b..368d3d9a9c8 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ManaEffect.java
@@ -24,10 +24,11 @@
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
-*/
-
+ */
package mage.abilities.effects.common;
+import java.util.ArrayList;
+import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@@ -45,26 +46,72 @@ import mage.game.events.ManaEvent;
*/
public abstract class ManaEffect extends OneShotEffect {
+ protected Mana createdMana;
+
public ManaEffect() {
super(Outcome.PutManaInPool);
+ createdMana = null;
}
public ManaEffect(final ManaEffect effect) {
super(effect);
+ this.createdMana = effect.createdMana == null ? null : effect.createdMana.copy();
}
- public abstract Mana getMana(Game game, Ability source);
+ /**
+ * Creates the mana the effect can produce or if that already has happened
+ * returns the mana the effect has created during its process of resolving
+ *
+ * @param game
+ * @param source
+ * @return
+ */
+ public Mana getMana(Game game, Ability source) {
+ if (createdMana == null) {
+ return createdMana = produceMana(false, game, source);
+ }
+ return createdMana;
+ }
/**
- * Only used for mana effects that decide which kind of mana is produced during resolution of the effect.
- *
+ * Returns the currently available max mana variations the effect can
+ * produce
+ *
+ * @param game
+ * @param source
+ * @return
+ */
+ public List getNetMana(Game game, Ability source) {
+ List netMana = new ArrayList<>();
+ Mana mana = produceMana(true, game, source);
+ if (mana != null) {
+ netMana.add(mana);
+ }
+ return netMana;
+ }
+
+ /**
+ * Produced the mana the effect can produce
+ *
+ * @param netMana true - produce the hypotetical possible mana for check of
+ * possible castable spells
+ * @param game
+ * @param source
+ * @return
+ */
+ public abstract Mana produceMana(boolean netMana, Game game, Ability source);
+
+ /**
+ * Only used for mana effects that decide which kind of mana is produced
+ * during resolution of the effect.
+ *
* @param mana
* @param game
* @param source
*/
public void checkToFirePossibleEvents(Mana mana, Game game, Ability source) {
- if (source.getAbilityType()==AbilityType.MANA) {
- for (Cost cost: source.getCosts()) {
+ if (source.getAbilityType() == AbilityType.MANA) {
+ for (Cost cost : source.getCosts()) {
if (cost instanceof TapSourceCost) {
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, source.getSourceId(), source.getSourceId(), source.getControllerId(), mana);
if (!game.replaceEvent(event)) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java
index 1df8ff9ff37..3672fd66b1e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutSourceEffect.java
@@ -5,7 +5,6 @@
*/
package mage.abilities.effects.common;
-import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
@@ -34,9 +33,8 @@ public class PhaseOutSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- MageObject sourceObject = source.getSourceObjectIfItStillExists(game);
- if (sourceObject instanceof Permanent) {
- Permanent permanent = (Permanent) sourceObject;
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
+ if (permanent != null) {
return permanent.phaseOut(game);
}
return false;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToSourceEffect.java
index c1f0ade0747..eab8e27cfdf 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToSourceEffect.java
@@ -54,6 +54,17 @@ public class PreventDamageToSourceEffect extends PreventionEffectImpl {
return new PreventDamageToSourceEffect(this);
}
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game); //To change body of generated methods, choose Tools | Templates.
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ }
+ }
+ }
+
@Override
public boolean apply(Game game, Ability source) {
return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java
index bff0d3cceed..76e56084f80 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java
@@ -18,8 +18,8 @@ import mage.game.permanent.Permanent;
*/
public class RedirectDamageFromSourceToTargetEffect extends RedirectionEffect {
- public RedirectDamageFromSourceToTargetEffect(Duration duration, int amountToRedirect, boolean oneUsage) {
- super(duration, amountToRedirect, oneUsage);
+ public RedirectDamageFromSourceToTargetEffect(Duration duration, int amountToRedirect, UsageType usageType) {
+ super(duration, amountToRedirect, usageType);
staticText = "The next " + amountToRedirect + " damage that would be dealt to {this} this turn is dealt to target creature you control instead.";
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java
index 6ba78d9a7ba..327233b1b8e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java
@@ -28,12 +28,18 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
+import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
+import mage.game.permanent.Permanent;
import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
/**
*
@@ -43,21 +49,25 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
private boolean tapped;
private boolean ownerControl;
+ private boolean haste;
public ReturnSourceFromGraveyardToBattlefieldEffect() {
this(false);
}
public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped) {
- super(Outcome.PutCreatureInPlay);
- this.tapped = tapped;
- setText();
+ this(tapped, true);
}
public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl) {
+ this(tapped, ownerControl, false);
+ }
+
+ public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl, boolean haste) {
super(Outcome.PutCreatureInPlay);
this.tapped = tapped;
this.ownerControl = ownerControl;
+ this.haste = haste;
setText();
}
@@ -65,6 +75,7 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
super(effect);
this.tapped = effect.tapped;
this.ownerControl = effect.ownerControl;
+ this.haste = effect.haste;
}
@Override
@@ -89,6 +100,14 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
}
if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null);
+ if (haste) {
+ Permanent permanent = game.getPermanent(card.getId());
+ if (permanent != null) {
+ ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
+ effect.setTargetPointer(new FixedTarget(permanent, game));
+ game.addEffect(effect, source);
+ }
+ }
}
return true;
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlSourceEffect.java
index 1f5b793fafc..ea3939f1b91 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlSourceEffect.java
@@ -33,6 +33,7 @@ import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
+import mage.players.Player;
/**
*
@@ -81,16 +82,17 @@ public class ReturnToBattlefieldUnderOwnerControlSourceEffect extends OneShotEff
@Override
public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
Card card = game.getCard(source.getSourceId());
- if (card != null) {
+ if (controller != null && card != null) {
// return only from public zones
switch (game.getState().getZone(card.getId())) {
case EXILED:
case COMMAND:
case GRAVEYARD:
if (zoneChangeCounter < 0 || game.getState().getZoneChangeCounter(card.getId()) == zoneChangeCounter) {
- Zone currentZone = game.getState().getZone(card.getId());
- if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), card.getOwnerId(), tapped)) {
+
+ if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null)) {
if (attacking) {
game.getCombat().addAttackingCreature(card.getId(), game);
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java
index ad80dffa498..94117fc3eb4 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java
@@ -103,7 +103,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
}
}
for (UUID copyId : copyIds) {
- game.getStack().remove(game.getSpell(copyId));
+ game.getStack().remove(game.getSpell(copyId), game);
}
return controller.moveCards(cards, Zone.HAND, source, game);
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/TapSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TapSourceEffect.java
index e510ae4844c..10b26d0099c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/TapSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/TapSourceEffect.java
@@ -63,10 +63,7 @@ public class TapSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(source.getSourceId());
- if (permanent == null) {
- permanent = game.getPermanentEntering(source.getSourceId());
- }
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent != null) {
if (withoutTrigger) {
permanent.setTapped(true);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
index 502b70186ba..f408e17033c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
@@ -31,8 +31,6 @@ import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
-import mage.filter.FilterPermanent;
-import mage.filter.StaticFilters;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game;
@@ -46,6 +44,11 @@ import mage.util.CardUtil;
*/
public class UntapLandsEffect extends OneShotEffect {
+ private static final FilterLandPermanent filter = new FilterLandPermanent("untapped lands");
+
+ static {
+ filter.add(new TappedPredicate());
+ }
private final int amount;
private final boolean upTo;
@@ -70,15 +73,12 @@ public class UntapLandsEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
- TargetLandPermanent target = new TargetLandPermanent(upTo ? 0 : amount, amount, StaticFilters.FILTER_LAND, true);
+ int tappedLands = game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game).size();
+ TargetLandPermanent target = new TargetLandPermanent(upTo ? 0 : Math.min(tappedLands, amount), amount, filter, true);
if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) {
// UI Shortcut: Check if any lands are already tapped. If there are equal/fewer than amount, give the option to add those in to be untapped now.
- FilterPermanent filter = new FilterLandPermanent();
- filter.add(new TappedPredicate());
- int tappedLands = game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game).size();
-
- if (tappedLands <= amount) {
+ if (tappedLands <= amount && upTo) {
if (controller.chooseUse(outcome, "Include your tapped lands to untap?", source, game)) {
for (Permanent land : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) {
target.addTarget(land.getId(), source, game);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllSourceEffect.java
index 442a7744264..59f0e2aef0a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllSourceEffect.java
@@ -58,7 +58,7 @@ public class CantBeBlockedByAllSourceEffect extends RestrictionEffect {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
- return source.getSourceId().equals(permanent.getId());
+ return permanent.equals(source.getSourcePermanentIfItStillExists(game));
}
@Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java
index 488568c71ee..b4ade73721d 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBlackZombieAdditionEffect.java
@@ -38,13 +38,33 @@ import mage.game.permanent.Permanent;
*/
public class BecomesBlackZombieAdditionEffect extends ContinuousEffectImpl {
+ private boolean giveBlackColor = true;
+
public BecomesBlackZombieAdditionEffect() {
super(Duration.Custom, Outcome.Neutral);
- staticText = "That creature is a black Zombie in addition to its other colors and types";
+ this.giveBlackColor = true;
+ updateText();
}
+ public BecomesBlackZombieAdditionEffect(boolean giveBlackColor) {
+ this();
+ this.giveBlackColor = giveBlackColor;
+ updateText();
+ }
+
+
public BecomesBlackZombieAdditionEffect(final BecomesBlackZombieAdditionEffect effect) {
super(effect);
+ this.giveBlackColor = effect.giveBlackColor;
+ updateText();
+ }
+
+ private void updateText() {
+ if (this.giveBlackColor) {
+ this.staticText = "That creature is a black Zombie in addition to its other colors and types";
+ } else {
+ this.staticText = "That creature is a Zombie in addition to its other types";
+ }
}
@Override
@@ -73,7 +93,7 @@ public class BecomesBlackZombieAdditionEffect extends ContinuousEffectImpl {
}
break;
case ColorChangingEffects_5:
- if (sublayer == SubLayer.NA) {
+ if (sublayer == SubLayer.NA && this.giveBlackColor) {
creature.getColor(game).setBlack(true);
}
break;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java
index 9eb451177d1..bf405711521 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java
@@ -52,21 +52,24 @@ import java.util.Set;
public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
protected Token token;
- protected String type;
+ protected String theyAreStillType;
private final FilterPermanent filter;
+ private boolean loseColor = true;
- public BecomesCreatureAllEffect(Token token, String type, FilterPermanent filter, Duration duration) {
+ public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor) {
super(duration, Outcome.BecomeCreature);
this.token = token;
- this.type = type;
+ this.theyAreStillType = theyAreStillType;
this.filter = filter;
+ this.loseColor = loseColor;
}
public BecomesCreatureAllEffect(final BecomesCreatureAllEffect effect) {
super(effect);
- token = effect.token.copy();
- type = effect.type;
+ this.token = effect.token.copy();
+ this.theyAreStillType = effect.theyAreStillType;
this.filter = effect.filter.copy();
+ this.loseColor = effect.loseColor;
}
@Override
@@ -94,6 +97,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
} else {
affectedPermanents = new HashSet<>(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game));
}
+
for(Permanent permanent : affectedPermanents) {
if (permanent != null) {
switch (layer) {
@@ -106,7 +110,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
}
}
}
- if (type == null) {
+ if (theyAreStillType == null) {
permanent.getSubtype(game).clear();
}
if (!token.getSubtype(game).isEmpty()) {
@@ -114,13 +118,22 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
}
}
break;
+
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
+ if (this.loseColor) {
+ permanent.getColor(game).setBlack(false);
+ permanent.getColor(game).setGreen(false);
+ permanent.getColor(game).setBlue(false);
+ permanent.getColor(game).setWhite(false);
+ permanent.getColor(game).setRed(false);
+ }
if (token.getColor(game).hasColor()) {
- permanent.getColor(game).setColor(token.getColor(game));
+ permanent.getColor(game).addColor(token.getColor(game));
}
}
break;
+
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
if (!token.getAbilities().isEmpty()) {
@@ -130,6 +143,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
}
}
break;
+
case PTChangingEffects_7:
if (sublayer == SubLayer.SetPT_7b) {
int power = token.getPower().getValue();
@@ -139,6 +153,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
permanent.getToughness().setValue(toughness);
}
}
+ break;
}
}
}
@@ -168,8 +183,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
sb.append(" become ");
}
sb.append(token.getDescription());
- if (type != null && !type.isEmpty()) {
- sb.append(". They're still ").append(type);
+ if (theyAreStillType != null && !theyAreStillType.isEmpty()) {
+ sb.append(". They're still ").append(theyAreStillType);
}
return sb.toString();
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java
index aca32daee2d..ae71f505861 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java
@@ -40,7 +40,7 @@ import mage.game.permanent.token.Token;
public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
public enum LoseType {
- NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE_AND_PT, ABILITIES_AND_PT
+ NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE, COLOR
}
protected Token token;
@@ -98,7 +98,7 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
- case ABILITIES_SUBTYPE_AND_PT:
+ case ABILITIES_SUBTYPE:
permanent.getSubtype(game).retainAll(SubType.getLandTypes(false));
break;
}
@@ -107,12 +107,12 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
permanent.getSubtype(game).add(t);
}
}
-
}
break;
+
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
- if (loseType == LoseType.ALL) {
+ if (loseType == LoseType.ALL || loseType == LoseType.COLOR) {
permanent.getColor(game).setBlack(false);
permanent.getColor(game).setGreen(false);
permanent.getColor(game).setBlue(false);
@@ -124,29 +124,29 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
}
}
break;
+
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
case ABILITIES:
- case ABILITIES_AND_PT:
- case ABILITIES_SUBTYPE_AND_PT:
+ case ABILITIES_SUBTYPE:
permanent.removeAllAbilities(source.getSourceId(), game);
break;
}
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, source.getSourceId(), game);
}
-
}
break;
+
case PTChangingEffects_7:
if (sublayer == SubLayer.SetPT_7b) {
permanent.getPower().setValue(token.getPower().getValue());
permanent.getToughness().setValue(token.getToughness().getValue());
- break;
}
+ break;
}
}
return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java
index 72e0ccf17c9..3f818e36597 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java
@@ -31,10 +31,10 @@ import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.ContinuousEffectImpl;
+import mage.abilities.keyword.FlyingAbility;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
-import mage.game.permanent.token.TokenImpl;
import mage.game.permanent.token.Token;
/**
@@ -43,34 +43,36 @@ import mage.game.permanent.token.Token;
public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements SourceEffect {
protected Token token;
- protected String type;
+ protected String theyAreStillType;
protected boolean losePreviousTypes;
protected DynamicValue power = null;
protected DynamicValue toughness = null;
- public BecomesCreatureSourceEffect(Token token, String type, Duration duration) {
- this(token, type, duration, false, false);
+ public BecomesCreatureSourceEffect(Token token, String theyAreStillType, Duration duration) {
+ this(token, theyAreStillType, duration, false, false);
}
- public BecomesCreatureSourceEffect(Token token, String type, Duration duration, boolean losePreviousTypes, boolean characterDefining) {
- this(token, type, duration, losePreviousTypes, characterDefining, null, null);
+ public BecomesCreatureSourceEffect(Token token, String theyAreStillType, Duration duration, boolean losePreviousTypes, boolean characterDefining) {
+ this(token, theyAreStillType, duration, losePreviousTypes, characterDefining, null, null);
}
- public BecomesCreatureSourceEffect(Token token, String type, Duration duration, boolean losePreviousTypes, boolean characterDefining, DynamicValue power, DynamicValue toughness) {
+ public BecomesCreatureSourceEffect(Token token, String theyAreStillType, Duration duration, boolean losePreviousTypes, boolean characterDefining, DynamicValue power, DynamicValue toughness) {
super(duration, Outcome.BecomeCreature);
this.characterDefining = characterDefining;
this.token = token;
- this.type = type;
+ this.theyAreStillType = theyAreStillType;
this.losePreviousTypes = losePreviousTypes;
this.power = power;
this.toughness = toughness;
setText();
+
+ this.addDependencyType(DependencyType.BecomeCreature);
}
public BecomesCreatureSourceEffect(final BecomesCreatureSourceEffect effect) {
super(effect);
this.token = effect.token.copy();
- this.type = effect.type;
+ this.theyAreStillType = effect.theyAreStillType;
this.losePreviousTypes = effect.losePreviousTypes;
if (effect.power != null) {
this.power = effect.power.copy();
@@ -108,10 +110,11 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
if (losePreviousTypes) {
permanent.getCardType().clear();
}
- for (CardType t : token.getCardType()) {
- permanent.addCardType(t);
+ for (CardType cardType : token.getCardType()) {
+ permanent.addCardType(cardType);
}
- if (type != null && type.isEmpty() || type == null && permanent.isLand()) {
+
+ if (theyAreStillType != null && theyAreStillType.isEmpty() || theyAreStillType == null && permanent.isLand()) {
permanent.getSubtype(game).retainAll(SubType.getLandTypes(false));
}
if (!token.getSubtype(game).isEmpty()) {
@@ -120,6 +123,7 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
permanent.setIsAllCreatureTypes(token.isAllCreatureTypes());
}
break;
+
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
if (token.getColor(game).hasColor()) {
@@ -127,19 +131,20 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
}
}
break;
+
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, source.getSourceId(), game);
}
-
}
break;
+
case PTChangingEffects_7:
if ((sublayer == SubLayer.CharacteristicDefining_7a && isCharacterDefining())
|| (sublayer == SubLayer.SetPT_7b && !isCharacterDefining())) {
if (power != null) {
- permanent.getPower().setValue(power.calculate(game, source, this));
+ permanent.getPower().setValue(power.calculate(game, source, this)); // check all other becomes to use calculate?
} else if (token.getPower() != null) {
permanent.getPower().setValue(token.getPower().getValue());
}
@@ -149,11 +154,15 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
permanent.getToughness().setValue(token.getToughness().getValue());
}
}
+ break;
}
+
return true;
+
} else if (duration == Duration.Custom) {
this.discard();
}
+
return false;
}
@@ -163,8 +172,8 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
}
private void setText() {
- if (type != null && !type.isEmpty()) {
- staticText = duration.toString() + " {this} becomes a " + token.getDescription() + " that's still a " + this.type;
+ if (theyAreStillType != null && !theyAreStillType.isEmpty()) {
+ staticText = duration.toString() + " {this} becomes a " + token.getDescription() + " that's still a " + this.theyAreStillType;
} else {
staticText = duration.toString() + " {this} becomes a " + token.getDescription();
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java
index d03847849d6..88617cd10d5 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java
@@ -39,6 +39,9 @@ import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
@@ -50,6 +53,7 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl {
private String rule;
private boolean withSource;
private boolean withSecondTarget;
+ private boolean destroyAttachedAuras;
private Map zoneChangeCounter = new HashMap<>();
private Map lockedControllers = new HashMap<>();
@@ -62,9 +66,14 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl {
}
public ExchangeControlTargetEffect(Duration duration, String rule, boolean withSource, boolean withSecondTarget) {
+ this(duration, rule, withSource, withSecondTarget, false);
+ }
+
+ public ExchangeControlTargetEffect(Duration duration, String rule, boolean withSource, boolean withSecondTarget, boolean destroyAttachedAuras) {
super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl);
this.withSource = withSource;
this.withSecondTarget = withSecondTarget;
+ this.destroyAttachedAuras = destroyAttachedAuras;
this.rule = rule;
}
@@ -73,6 +82,7 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl {
this.rule = effect.rule;
this.withSource = effect.withSource;
this.withSecondTarget = effect.withSecondTarget;
+ this.destroyAttachedAuras = effect.destroyAttachedAuras;
this.lockedControllers = new HashMap<>(effect.lockedControllers);
this.zoneChangeCounter = new HashMap<>(effect.zoneChangeCounter);
}
@@ -141,6 +151,16 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl {
}
permanent.changeControllerId(lockedControllers.get(permanent.getId()), game);
permanent.getAbilities().setControllerId(lockedControllers.get(permanent.getId()));
+ if (destroyAttachedAuras) {
+ FilterPermanent filter = new FilterPermanent();
+ filter.add(new SubtypePredicate(SubType.AURA));
+ for (UUID attachmentId : new HashSet<>(permanent.getAttachments())) {
+ Permanent attachment = game.getPermanent(attachmentId);
+ if (attachment != null && filter.match(attachment, game)) {
+ attachment.destroy(source.getSourceId(), game, false);
+ }
+ }
+ }
}
if (!toDelete.isEmpty()) {
for (UUID uuid : toDelete) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
index 4e871839bbb..c9245e98340 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
@@ -92,6 +92,13 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
@Override
public void init(Ability source, Game game) {
super.init(source, game);
+ if (!onCard && Duration.WhileOnBattlefield != duration) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ return;
+ }
+ }
if (affectedObjectsSet) {
Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
index bbb09b1281d..6f5d03f33b2 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
@@ -12,38 +12,50 @@ import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
+
/**
*
* @author Noahsark
*/
-public class LoseAbilitySourceEffect extends ContinuousEffectImpl{
+public class LoseAbilitySourceEffect extends ContinuousEffectImpl {
protected Ability ability;
- public LoseAbilitySourceEffect(Ability ability){
+ public LoseAbilitySourceEffect(Ability ability) {
this(ability, Duration.WhileOnBattlefield);
}
- public LoseAbilitySourceEffect(Ability ability, Duration duration){
+ public LoseAbilitySourceEffect(Ability ability, Duration duration) {
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility);
this.ability = ability;
staticText = "{this} loses " + ability.getRule() + ' ' + duration.toString();
}
- public LoseAbilitySourceEffect(final LoseAbilitySourceEffect effect){
+ public LoseAbilitySourceEffect(final LoseAbilitySourceEffect effect) {
super(effect);
this.ability = effect.ability.copy();
}
@Override
- public LoseAbilitySourceEffect copy(){
+ public LoseAbilitySourceEffect copy() {
return new LoseAbilitySourceEffect(this);
}
@Override
- public boolean apply(Game game, Ability source){
+ public void init(Ability source, Game game) {
+ super.init(source, game); //To change body of generated methods, choose Tools | Templates.
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ }
+ }
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
- if (permanent != null){
+ if (permanent != null) {
// 112.10
while (permanent.getAbilities().remove(ability)) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java
index c1170ddc95d..af89fd318e1 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseCreatureTypeSourceEffect.java
@@ -69,6 +69,17 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
return new LoseCreatureTypeSourceEffect(this);
}
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game); //To change body of generated methods, choose Tools | Templates.
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ }
+ }
+ }
+
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
if (dynamicValue.calculate(game, source, this) >= lessThan) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessSourceEffect.java
index a13e3178d6b..adcd4b7dfba 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SwitchPowerToughnessSourceEffect.java
@@ -1,16 +1,16 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
- *
+ *
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
- *
+ *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@@ -20,19 +20,19 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
+ *
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common.continuous;
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
-import mage.abilities.Ability;
-import mage.abilities.effects.ContinuousEffectImpl;
import mage.game.Game;
import mage.game.permanent.Permanent;
@@ -55,14 +55,29 @@ public class SwitchPowerToughnessSourceEffect extends ContinuousEffectImpl {
return new SwitchPowerToughnessSourceEffect(this);
}
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game); //To change body of generated methods, choose Tools | Templates.
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ // If source permanent is no longer onto battlefield discard the effect
+ if (source.getSourcePermanentIfItStillExists(game) == null) {
+ discard();
+ }
+ }
+ }
+
@Override
public boolean apply(Game game, Ability source) {
- Permanent target = game.getPermanent(source.getSourceId());
- if (target != null) {
- int power = target.getPower().getValue();
- target.getPower().setValue(target.getToughness().getValue());
- target.getToughness().setValue(power);
+ Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
+ if (sourcePermanent != null) {
+ int power = sourcePermanent.getPower().getValue();
+ sourcePermanent.getPower().setValue(sourcePermanent.getToughness().getValue());
+ sourcePermanent.getToughness().setValue(power);
return true;
+ } else {
+ if (duration.isOnlyValidIfNoZoneChange()) {
+ discard();
+ }
}
return false;
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
index 0b7af33808d..c66870598c2 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
@@ -53,6 +53,7 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
private FilterCard filter;
private int amount;
private final boolean upTo;
+ private boolean onlyControlled;
public SpellsCostReductionAllEffect(int amount) {
this(new FilterCard("Spells"), amount);
@@ -63,19 +64,24 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
}
public SpellsCostReductionAllEffect(FilterCard filter, int amount, boolean upTo) {
+ this(filter, amount, upTo, false);
+ }
+
+ public SpellsCostReductionAllEffect(FilterCard filter, int amount, boolean upTo, boolean onlyControlled) {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
this.filter = filter;
this.amount = amount;
this.upTo = upTo;
-
+ this.onlyControlled = onlyControlled;
this.staticText = filter.getMessage() + " cost " + (upTo ? "up to " : "") + '{' + amount + "} less to cast";
}
- protected SpellsCostReductionAllEffect(SpellsCostReductionAllEffect effect) {
+ protected SpellsCostReductionAllEffect(final SpellsCostReductionAllEffect effect) {
super(effect);
this.filter = effect.filter;
this.amount = effect.amount;
this.upTo = effect.upTo;
+ this.onlyControlled = effect.onlyControlled;
}
@Override
@@ -136,6 +142,9 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
+ if (onlyControlled && abilityToModify.getControllerId().equals(source.getControllerId())) {
+ return false;
+ }
if (abilityToModify instanceof SpellAbility) {
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
if (spell != null) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java
index 71185730ca1..4a8fdf40d0b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllOfChosenSubtypeEffect.java
@@ -19,7 +19,11 @@ import mage.game.Game;
public class SpellsCostReductionAllOfChosenSubtypeEffect extends SpellsCostReductionAllEffect {
public SpellsCostReductionAllOfChosenSubtypeEffect(FilterCard filter, int amount) {
- super(filter, amount);
+ this(filter, amount, false);
+ }
+
+ public SpellsCostReductionAllOfChosenSubtypeEffect(FilterCard filter, int amount, boolean onlyControlled) {
+ super(filter, amount, false, onlyControlled);
}
public SpellsCostReductionAllOfChosenSubtypeEffect(final SpellsCostReductionAllOfChosenSubtypeEffect effect) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersSourceEffect.java
index 10b24748f45..3697e18b01c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersSourceEffect.java
@@ -39,7 +39,7 @@ import mage.game.permanent.Permanent;
* @author TheElk801
*/
public class RemoveAllCountersSourceEffect extends OneShotEffect {
-
+
private final CounterType counterType;
public RemoveAllCountersSourceEffect(CounterType counterType) {
@@ -55,13 +55,13 @@ public class RemoveAllCountersSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(source.getSourceId());
- if(permanent != null) {
- int count = permanent.getCounters(game).getCount(counterType);
- permanent.removeCounters(counterType.getName(), count, game);
- return true;
- }
- return false;
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
+ if (permanent != null) {
+ int count = permanent.getCounters(game).getCount(counterType);
+ permanent.removeCounters(counterType.getName(), count, game);
+ return true;
+ }
+ return false;
}
@Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java
index 2e4feb8c523..35fa7df1f58 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java
@@ -56,7 +56,7 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Permanent permanent = game.getPermanent(source.getSourceId());
+ Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent != null) {
int toRemove = Math.min(counter.getCount(), permanent.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
@@ -67,18 +67,20 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
}
return true;
}
- Card card = game.getCard(source.getSourceId());
- if (card != null) {
- int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
- if (toRemove > 0) {
- card.removeCounters(counter.getName(), toRemove, game);
- if (!game.isSimulation()) {
- game.informPlayers("Removed " + toRemove + ' ' + counter.getName()
- + " counter from " + card.getLogName()
- + " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
+ if (!(source.getSourceObject(game) instanceof Permanent)) {
+ Card card = game.getCard(source.getSourceId());
+ if (card != null) {
+ int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
+ if (toRemove > 0) {
+ card.removeCounters(counter.getName(), toRemove, game);
+ if (!game.isSimulation()) {
+ game.informPlayers("Removed " + toRemove + ' ' + counter.getName()
+ + " counter from " + card.getLogName()
+ + " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
+ }
}
+ return true;
}
- return true;
}
return false;
}
diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java
index b70c5895679..fa6a28a5f90 100644
--- a/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java
@@ -53,7 +53,7 @@ public class SweepEffect extends OneShotEffect {
public SweepEffect(SubType sweepSubtype) {
super(Outcome.Benefit);
this.sweepSubtype = sweepSubtype;
- this.staticText = "Sweep - Return any number of " + sweepSubtype + (sweepSubtype.getDescription().endsWith("s") ? "" : "s") + " you control to their owner's hand";
+ this.staticText = "Sweep — Return any number of " + sweepSubtype + (sweepSubtype.getDescription().endsWith("s") ? "" : "s") + " you control to their owner's hand";
}
public SweepEffect(final SweepEffect effect) {
diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java
new file mode 100644
index 00000000000..7a2822f2f8b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalColorlessManaEffect.java
@@ -0,0 +1,29 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package mage.abilities.effects.mana;
+
+import mage.Mana;
+import mage.abilities.mana.builder.ConditionalManaBuilder;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class AddConditionalColorlessManaEffect extends AddConditionalManaEffect {
+
+ public AddConditionalColorlessManaEffect(int amount, ConditionalManaBuilder manaBuilder) {
+ super(Mana.ColorlessMana(amount), manaBuilder);
+ }
+
+ public AddConditionalColorlessManaEffect(final AddConditionalColorlessManaEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public AddConditionalColorlessManaEffect copy() {
+ return new AddConditionalColorlessManaEffect(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java
similarity index 87%
rename from Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java
index b3dedd8d6c5..3ee00370fff 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaEffect.java
@@ -3,10 +3,11 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.game.Game;
import mage.players.Player;
@@ -49,7 +50,11 @@ public class AddConditionalManaEffect extends ManaEffect {
}
@Override
- public Mana getMana(Game game, Ability source) {
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
return manaBuilder.setMana(mana, source, game).build();
}
+
+ public Mana getMana() {
+ return mana;
+ }
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java
similarity index 72%
rename from Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaOfAnyColorEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java
index 3f683a25de9..8cdb5f50276 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddConditionalManaOfAnyColorEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java
@@ -25,12 +25,14 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
+import mage.ConditionalMana;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.common.ManaEffect;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.choices.ChoiceColor;
import mage.game.Game;
@@ -63,9 +65,9 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
staticText = "Add "
+ (amount instanceof StaticValue ? (CardUtil.numberToText(((StaticValue) amount).toString())) : "")
+ " mana "
- + (oneChoice ? "of any"
- + (amount instanceof StaticValue && (((StaticValue) amount).toString()).equals("1") ? "" : " one")
- + " color" : "in any combination of colors")
+ + (oneChoice || (amount instanceof StaticValue && (((StaticValue) amount).toString()).equals("1"))
+ ? "of any" + (amount instanceof StaticValue && (((StaticValue) amount).toString()).equals("1") ? "" : " one") + " color"
+ : "in any combination of colors")
+ ". " + manaBuilder.getRule();
}
@@ -84,39 +86,44 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
- if (controller == null) {
- return false;
+ if (controller != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ controller.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
}
+ return false;
+ }
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
+ return null;
+ }
+ ConditionalMana mana = null;
int value = amount.calculate(game, source, this);
- boolean result = false;
ChoiceColor choice = new ChoiceColor(true);
-
for (int i = 0; i < value; i++) {
- controller.choose(outcome, choice, game);
if (choice.getChoice() == null) {
- return false;
+ controller.choose(outcome, choice, game);
}
- Mana mana = choice.getMana(1);
- if (mana != null) {
- mana = manaBuilder.setMana(mana, source, game).build();
+ if (choice.getChoice() == null) {
+ return null;
}
- if (mana != null) {
- checkToFirePossibleEvents(mana, game, source);
- controller.getManaPool().addMana(mana, game, source);
- result = true;
- }
- if (!oneChoice) {
+ if (oneChoice) {
+ mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build());
+ break;
+ } else {
+ if (mana == null) {
+ mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(1), source, game).build());
+ } else {
+ mana.add(choice.getMana(1));
+ }
choice.clearChoice();
}
}
- return result;
- }
+ return mana;
- @Override
- public Mana getMana(Game game, Ability source) {
- //TODO: TAP_FOR_MANA Event does not support currently to get an amount > 1 of conditional mana
- return null;
}
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaAnyColorAttachedControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaAnyColorAttachedControllerEffect.java
similarity index 70%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaAnyColorAttachedControllerEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaAnyColorAttachedControllerEffect.java
index 0e3e6d05d6b..2fdf2db8076 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaAnyColorAttachedControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaAnyColorAttachedControllerEffect.java
@@ -25,10 +25,13 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
+import java.util.ArrayList;
+import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
import mage.choices.ChoiceColor;
import mage.game.Game;
import mage.game.permanent.Permanent;
@@ -53,14 +56,12 @@ public class AddManaAnyColorAttachedControllerEffect extends ManaEffect {
public boolean apply(Game game, Ability source) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null) {
- Permanent land = game.getPermanent(enchantment.getAttachedTo());
- if (land != null) {
- Player player = game.getPlayer(land.getControllerId());
- ChoiceColor choice = new ChoiceColor();
- if (player != null && player.choose(outcome, choice, game)) {
- Mana mana = choice.getMana(1);
- checkToFirePossibleEvents(mana, game, source);
- player.getManaPool().addMana(mana, game, source);
+ Permanent permanentattachedTo = game.getPermanent(enchantment.getAttachedTo());
+ if (permanentattachedTo != null) {
+ Player player = game.getPlayer(permanentattachedTo.getControllerId());
+ if (player != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ player.getManaPool().addMana(getMana(game, source), game, source);
return true;
}
}
@@ -74,8 +75,30 @@ public class AddManaAnyColorAttachedControllerEffect extends ManaEffect {
}
@Override
- public Mana getMana(Game game, Ability source) {
- return null;
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Permanent enchantment = game.getPermanent(source.getSourceId());
+ if (enchantment != null) {
+ Permanent land = game.getPermanent(enchantment.getAttachedTo());
+ if (land != null) {
+ Player player = game.getPlayer(land.getControllerId());
+ ChoiceColor choice = new ChoiceColor();
+ if (player != null && player.choose(outcome, choice, game)) {
+ return choice.getMana(1);
+ }
+ }
+ }
+ return new Mana();
+ }
+
+ @Override
+ public List getNetMana(Game game, Ability source) {
+ ArrayList netMana = new ArrayList<>();
+ netMana.add(Mana.GreenMana(1));
+ netMana.add(Mana.WhiteMana(1));
+ netMana.add(Mana.BlueMana(1));
+ netMana.add(Mana.RedMana(1));
+ netMana.add(Mana.BlackMana(1));
+ return netMana;
}
}
diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
new file mode 100644
index 00000000000..eefb08c618d
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
@@ -0,0 +1,54 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package mage.abilities.effects.mana;
+
+import mage.Mana;
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
+import mage.constants.ColoredManaSymbol;
+import mage.game.Game;
+import mage.players.Player;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class AddManaChosenColorEffect extends ManaEffect {
+
+ public AddManaChosenColorEffect() {
+ super();
+ staticText = "Add one mana of the chosen color";
+ }
+
+ public AddManaChosenColorEffect(final AddManaChosenColorEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player player = game.getPlayer(source.getControllerId());
+ if (player != null) {
+ player.getManaPool().addMana(getMana(game, source), game, source);
+ }
+ return true;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color");
+ if (color != null) {
+ return new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0)));
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public AddManaChosenColorEffect copy() {
+ return new AddManaChosenColorEffect(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java
similarity index 88%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java
index 0bb378b1edb..55e982a3291 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java
@@ -25,14 +25,16 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.common.ManaEffect;
import mage.constants.ColoredManaSymbol;
import mage.game.Game;
import mage.players.Player;
@@ -90,6 +92,17 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
+ Player player = game.getPlayer(source.getControllerId());
+ if (player != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ player.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Mana mana = new Mana();
@@ -111,16 +124,17 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
}
}
}
- checkToFirePossibleEvents(mana, game, source);
- player.getManaPool().addMana(mana, game, source);
- return true;
+
+ return mana;
}
- return false;
+ return null;
}
@Override
- public Mana getMana(Game game, Ability source) {
- return null;
+ public List getNetMana(Game game, Ability source) {
+ ArrayList netMana = new ArrayList<>();
+ netMana.add(new Mana(0, 0, 0, 0, 0, 0, amount.calculate(game, source, this), 0));
+ return netMana;
}
private String setText() {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java
similarity index 98%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java
index bf66ff3607b..55749f4618f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyColorEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java
@@ -25,7 +25,7 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
@@ -91,7 +91,7 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect {
}
@Override
- public Mana getMana() {
+ public Mana getManaTemplate() {
return new Mana(0, 0, 0, 0, 0, 0, amount, 0);
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java
similarity index 85%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java
index 2d84bfff046..2885f791b4c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyTypeProducedEffect.java
@@ -25,10 +25,11 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
import mage.choices.Choice;
import mage.choices.ChoiceColor;
import mage.game.Game;
@@ -58,6 +59,24 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
if (targetController == null) {
return false;
}
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ targetController.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ if (netMana) {
+ return null;
+ }
+ Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+ if (permanent != null) {
+ Player targetController = game.getPlayer(permanent.getControllerId());
+ if (targetController == null) {
+ return null;
+ }
Mana types = (Mana) this.getValue("mana");
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
@@ -80,15 +99,16 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
if (types.getColorless() > 0) {
choice.getChoices().add("Colorless");
}
+ Mana newMana = new Mana();
if (!choice.getChoices().isEmpty()) {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!targetController.choose(outcome, choice, game)) {
- return false;
+ return null;
}
}
- Mana newMana = new Mana();
+
switch (choice.getChoice()) {
case "Black":
newMana.setBlack(1);
@@ -109,13 +129,10 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
newMana.setColorless(1);
break;
}
- checkToFirePossibleEvents(newMana, game, source);
- targetController.getManaPool().addMana(newMana, game, source);
-
}
- return true;
+ return newMana;
}
- return false;
+ return null;
}
@Override
@@ -123,8 +140,4 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
return new AddManaOfAnyTypeProducedEffect(this);
}
- @Override
- public Mana getMana(Game game, Ability source) {
- return null;
- }
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolSourceControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java
similarity index 97%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolSourceControllerEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java
index fe3e3195f61..a2d8eabde44 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolSourceControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolSourceControllerEffect.java
@@ -4,7 +4,7 @@
* and open the template in the editor.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolTargetControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java
similarity index 74%
rename from Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolTargetControllerEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java
index e7943c88b1e..44bfec584d2 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AddManaToManaPoolTargetControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaToManaPoolTargetControllerEffect.java
@@ -3,11 +3,11 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
-
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
import mage.game.Game;
import mage.players.Player;
@@ -15,28 +15,30 @@ import mage.players.Player;
*
* @author LevelX2
*/
-
public class AddManaToManaPoolTargetControllerEffect extends ManaEffect {
protected Mana mana;
protected boolean emptyOnlyOnTurnsEnd;
-
+
public AddManaToManaPoolTargetControllerEffect(Mana mana, String textManaPoolOwner) {
this(mana, textManaPoolOwner, false);
}
+
/**
* Adds mana to the mana pool of target pointer player
- *
+ *
* @param mana mana that will be added to the pool
- * @param textManaPoolOwner text that references to the mana pool owner (e.g. "damaged player's")
- * @param emptyOnTurnsEnd if set, the mana will empty only on end of turnstep
- *
- */
+ * @param textManaPoolOwner text that references to the mana pool owner
+ * (e.g. "damaged player's")
+ * @param emptyOnTurnsEnd if set, the mana will empty only on end of
+ * turnstep
+ *
+ */
public AddManaToManaPoolTargetControllerEffect(Mana mana, String textManaPoolOwner, boolean emptyOnTurnsEnd) {
super();
this.mana = mana;
this.emptyOnlyOnTurnsEnd = emptyOnTurnsEnd;
- this.staticText = (textManaPoolOwner.equals("their")?"that player adds ":"add ") + mana.toString() + " to " + textManaPoolOwner + " mana pool";
+ this.staticText = (textManaPoolOwner.equals("their") ? "that player adds " : "add ") + mana.toString() + " to " + textManaPoolOwner + " mana pool";
}
public AddManaToManaPoolTargetControllerEffect(final AddManaToManaPoolTargetControllerEffect effect) {
@@ -54,15 +56,17 @@ public class AddManaToManaPoolTargetControllerEffect extends ManaEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
- player.getManaPool().addMana(mana, game, source, emptyOnlyOnTurnsEnd);
+ player.getManaPool().addMana(getMana(game, source), game, source, emptyOnlyOnTurnsEnd);
return true;
}
return false;
}
@Override
- public Mana getMana(Game game, Ability source) {
- return mana;
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ if (netMana) {
+ return null;
+ }
+ return mana.copy();
}
-
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/BasicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java
similarity index 56%
rename from Mage/src/main/java/mage/abilities/effects/common/BasicManaEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java
index 5bcd549778a..98a361c12e7 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/BasicManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/BasicManaEffect.java
@@ -1,29 +1,31 @@
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.ConditionalMana;
import mage.Mana;
import mage.abilities.Ability;
+import mage.abilities.effects.common.ManaEffect;
import mage.game.Game;
public class BasicManaEffect extends ManaEffect {
- protected Mana mana;
+ protected Mana manaTemplate;
public BasicManaEffect(Mana mana) {
super();
- this.mana = mana;
+ this.manaTemplate = mana;
staticText = "add " + mana.toString();
}
public BasicManaEffect(ConditionalMana conditionalMana) {
super();
- this.mana = conditionalMana;
- staticText = "add " + mana.toString() + " " + conditionalMana.getDescription();
+ this.manaTemplate = conditionalMana;
+ staticText = "add " + manaTemplate.toString() + " " + conditionalMana.getDescription();
}
public BasicManaEffect(final BasicManaEffect effect) {
super(effect);
- this.mana = effect.mana.copy();
+ this.manaTemplate = effect.manaTemplate.copy();
+
}
@Override
@@ -33,16 +35,17 @@ public class BasicManaEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
- game.getPlayer(source.getControllerId()).getManaPool().addMana(mana, game, source);
+ game.getPlayer(source.getControllerId()).getManaPool().addMana(getMana(game, source), game, source);
return true;
}
- public Mana getMana() {
- return mana;
+ public Mana getManaTemplate() {
+ return manaTemplate;
}
@Override
- public Mana getMana(Game game, Ability source) {
- return mana;
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ return manaTemplate.copy();
}
+
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java
similarity index 84%
rename from Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysManaEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java
index 29177424250..73a3eba235c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/DoUnlessAnyPlayerPaysManaEffect.java
@@ -3,14 +3,17 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.costs.Cost;
+import mage.abilities.effects.common.ManaEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
@@ -69,6 +72,24 @@ public class DoUnlessAnyPlayerPaysManaEffect extends ManaEffect {
return false;
}
+ @Override
+ public List getNetMana(Game game, Ability source) {
+ if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game)) {
+ return manaEffect.getNetMana(game, source);
+ }
+ return new ArrayList<>();
+ }
+
+ @Override
+ public Mana getMana(Game game, Ability source) {
+ return manaEffect.getMana(game, source);
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ return manaEffect.produceMana(netMana, game, source);
+ }
+
protected Player getPayingPlayer(Game game, Ability source) {
return game.getPlayer(source.getControllerId());
}
@@ -81,11 +102,6 @@ public class DoUnlessAnyPlayerPaysManaEffect extends ManaEffect {
return manaEffect.getText(mode) + " unless any player pays " + cost.getText();
}
- @Override
- public Mana getMana(Game game, Ability source) {
- return manaEffect.getMana(game, source);
- }
-
@Override
public ManaEffect copy() {
return new DoUnlessAnyPlayerPaysManaEffect(this);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java
similarity index 85%
rename from Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java
rename to Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java
index 4ce3ece908a..a700463320c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java
@@ -25,7 +25,7 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
-package mage.abilities.effects.common;
+package mage.abilities.effects.mana;
import mage.Mana;
import mage.abilities.Ability;
@@ -64,10 +64,10 @@ public class DynamicManaEffect extends BasicManaEffect {
* @param mana
* @param amount
* @param text
- * @param oneChoice is all mana from the same colour or if false the player
- * can choose different colours
+ * @param oneChoice is all manaTemplate from the same colour or if false the
+ * player can choose different colours
* @param netAmount a dynamic value that calculates the possible available
- * mana (e.g. if you have to pay by removing counters from source)
+ * manaTemplate (e.g. if you have to pay by removing counters from source)
*/
public DynamicManaEffect(Mana mana, DynamicValue amount, String text, boolean oneChoice, DynamicValue netAmount) {
super(mana);
@@ -96,9 +96,8 @@ public class DynamicManaEffect extends BasicManaEffect {
@Override
public boolean apply(Game game, Ability source) {
- Mana computedMana = computeMana(false, game, source);
- checkToFirePossibleEvents(computedMana, game, source);
- game.getPlayer(source.getControllerId()).getManaPool().addMana(computedMana, game, source);
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ game.getPlayer(source.getControllerId()).getManaPool().addMana(getMana(game, source), game, source);
return true;
}
@@ -111,33 +110,29 @@ public class DynamicManaEffect extends BasicManaEffect {
}
@Override
- public Mana getMana(Game game, Ability source) {
- return null;
- }
-
- public Mana computeMana(boolean netMana, Game game, Ability source) {
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
Mana computedMana = new Mana();
int count;
if (netMana && netAmount != null) {
- // calculate the maximum available mana
+ // calculate the maximum available manaTemplate
count = netAmount.calculate(game, source, this);
} else {
count = amount.calculate(game, source, this);
}
- if (mana.getBlack() > 0) {
+ if (manaTemplate.getBlack() > 0) {
computedMana.setBlack(count);
- } else if (mana.getBlue() > 0) {
+ } else if (manaTemplate.getBlue() > 0) {
computedMana.setBlue(count);
- } else if (mana.getGreen() > 0) {
+ } else if (manaTemplate.getGreen() > 0) {
computedMana.setGreen(count);
- } else if (mana.getRed() > 0) {
+ } else if (manaTemplate.getRed() > 0) {
computedMana.setRed(count);
- } else if (mana.getWhite() > 0) {
+ } else if (manaTemplate.getWhite() > 0) {
computedMana.setWhite(count);
- } else if (mana.getColorless() > 0) {
+ } else if (manaTemplate.getColorless() > 0) {
computedMana.setColorless(count);
- } else if (mana.getAny() > 0) {
+ } else if (manaTemplate.getAny() > 0) {
if (netMana) {
computedMana.setAny(count);
} else {
diff --git a/Mage/src/main/java/mage/abilities/keyword/BattalionAbility.java b/Mage/src/main/java/mage/abilities/keyword/BattalionAbility.java
index 9b78f558631..a9fedfd858a 100644
--- a/Mage/src/main/java/mage/abilities/keyword/BattalionAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/BattalionAbility.java
@@ -72,7 +72,7 @@ public class BattalionAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
- return new StringBuilder("Battalion - Whenever {this} and at least two other creatures attack, ").append(super.getRule()).toString() ;
+ return new StringBuilder("Battalion — Whenever {this} and at least two other creatures attack, ").append(super.getRule()).toString() ;
}
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/BloodrushAbility.java b/Mage/src/main/java/mage/abilities/keyword/BloodrushAbility.java
index dafb43c8e81..ea9fda3ca01 100644
--- a/Mage/src/main/java/mage/abilities/keyword/BloodrushAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/BloodrushAbility.java
@@ -65,7 +65,7 @@ public class BloodrushAbility extends ActivatedAbilityImpl {
@Override
public String getRule() {
- StringBuilder sb = new StringBuilder("Bloodrush - ");
+ StringBuilder sb = new StringBuilder("Bloodrush — ");
sb.append(super.getRule());
return sb.toString();
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java
index f181894b99b..a88831f2253 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java
@@ -60,7 +60,7 @@ public class ChannelAbility extends ActivatedAbilityImpl {
@Override
public String getRule() {
- StringBuilder sb = new StringBuilder("Channel - ");
+ StringBuilder sb = new StringBuilder("Channel — ");
sb.append(super.getRule());
if(this.timing == TimingRule.SORCERY) {
sb.append(" Activate this ability only any time you could cast a sorcery.");
diff --git a/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java b/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java
index 083a80484b7..1a8d5d70583 100644
--- a/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java
@@ -103,6 +103,6 @@ public class HeroicAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
- return new StringBuilder("Heroic - Whenever you cast a spell that targets {this}, ").append(super.getRule()).toString();
+ return new StringBuilder("Heroic — Whenever you cast a spell that targets {this}, ").append(super.getRule()).toString();
}
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
index 23a8b876bb1..5ac808f7dad 100644
--- a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
@@ -71,6 +71,6 @@ public class InspiredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
- return "Inspired - Whenever {this} becomes untapped, " + super.getRule();
+ return "Inspired — Whenever {this} becomes untapped, " + super.getRule();
}
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java
index 46471af292f..3546cc592cb 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java
@@ -27,6 +27,8 @@
*/
package mage.abilities.keyword;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.ObjectColor;
@@ -53,6 +55,7 @@ public class ProtectionAbility extends StaticAbility {
protected Filter filter;
protected boolean removeAuras;
+ protected static List colors = new ArrayList<>();
protected UUID auraIdNotToBeRemoved; // defines an Aura objectId that will not be removed from this protection ability
public ProtectionAbility(Filter filter) {
@@ -72,12 +75,15 @@ public class ProtectionAbility extends StaticAbility {
public static ProtectionAbility from(ObjectColor color) {
FilterObject filter = new FilterObject(color.getDescription());
filter.add(new ColorPredicate(color));
+ colors.add(color);
return new ProtectionAbility(filter);
}
public static ProtectionAbility from(ObjectColor color1, ObjectColor color2) {
FilterObject filter = new FilterObject(color1.getDescription() + " and from " + color2.getDescription());
filter.add(Predicates.or(new ColorPredicate(color1), new ColorPredicate(color2)));
+ colors.add(color1);
+ colors.add(color2);
return new ProtectionAbility(filter);
}
@@ -140,6 +146,8 @@ public class ProtectionAbility extends StaticAbility {
return removeAuras;
}
+ public List getColors() { return colors; }
+
public UUID getAuraIdNotToBeRemoved() {
return auraIdNotToBeRemoved;
}
diff --git a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java
index 239706abad4..b43eb548597 100644
--- a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java
@@ -35,7 +35,6 @@ import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileSourceEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
-import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TimingRule;
@@ -63,9 +62,8 @@ import mage.game.events.ZoneChangeEvent;
public class UnearthAbility extends ActivatedAbilityImpl {
public UnearthAbility(ManaCosts costs) {
- super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), costs);
+ super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false, true, true), costs);
this.timing = TimingRule.SORCERY;
- this.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom));
this.addEffect(new CreateDelayedTriggeredAbilityEffect(new UnearthDelayedTriggeredAbility()));
this.addEffect(new UnearthLeavesBattlefieldEffect());
}
diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java
index f6c63bc2d46..dbbf9bb9103 100644
--- a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java
@@ -29,8 +29,8 @@ package mage.abilities.mana;
import mage.abilities.condition.Condition;
import mage.abilities.costs.Cost;
-import mage.abilities.effects.common.AddConditionalColorlessManaEffect;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.AddConditionalColorlessManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.Zone;
import mage.game.Game;
@@ -38,7 +38,7 @@ public class ActivateIfConditionManaAbility extends ActivatedManaAbilityImpl {
public ActivateIfConditionManaAbility(Zone zone, BasicManaEffect effect, Cost cost, Condition condition) {
super(zone, effect, cost);
- this.netMana.add(effect.getMana());
+ this.netMana.add(effect.getManaTemplate());
this.condition = condition;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java
index dc5f46335a9..d647995c9b9 100644
--- a/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ActivateOncePerTurnManaAbility.java
@@ -29,8 +29,8 @@ package mage.abilities.mana;
import mage.Mana;
import mage.abilities.costs.Cost;
-import mage.abilities.effects.common.AddManaOfAnyColorEffect;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.Zone;
import mage.game.Game;
@@ -42,7 +42,7 @@ public class ActivateOncePerTurnManaAbility extends ActivatedManaAbilityImpl {
public ActivateOncePerTurnManaAbility(Zone zone, BasicManaEffect effect, Cost cost) {
super(zone, effect, cost);
- this.netMana.add(effect.getMana());
+ this.netMana.add(effect.getManaTemplate());
this.maxActivationsPerTurn = 1;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
index e25aadbe696..9f7c294c6c3 100644
--- a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
@@ -33,6 +33,7 @@ import java.util.UUID;
import mage.Mana;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
+import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ManaEffect;
import mage.constants.AbilityType;
import mage.constants.AsThoughEffectType;
@@ -95,6 +96,13 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
*/
@Override
public List getNetMana(Game game) {
+ if (netMana.isEmpty()) {
+ for (Effect effect : getEffects()) {
+ if (effect instanceof ManaEffect) {
+ netMana.addAll(((ManaEffect) effect).getNetMana(game, this));
+ }
+ }
+ }
return netMana;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java
index b2f4056fe18..14b9cec5104 100644
--- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java
@@ -105,6 +105,18 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ controller.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Mana mana = new Mana();
Mana types = getManaTypes(game, source);
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
@@ -143,12 +155,11 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
- if (!player.choose(outcome, choice, game)) {
- return false;
+ if (player == null || !player.choose(outcome, choice, game)) {
+ return null;
}
}
if (choice.getChoice() != null) {
- Mana mana = new Mana();
switch (choice.getChoice()) {
case "Black":
mana.setBlack(1);
@@ -169,16 +180,9 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
mana.setColorless(1);
break;
}
- checkToFirePossibleEvents(mana, game, source);
- player.getManaPool().addMana(mana, game, source);
}
}
- return true;
- }
-
- @Override
- public Mana getMana(Game game, Ability source) {
- return null;
+ return mana;
}
private Mana getManaTypes(Game game, Ability source) {
@@ -205,6 +209,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
return types;
}
+ @Override
public List getNetMana(Game game, Ability source) {
List netManas = new ArrayList<>();
Mana types = getManaTypes(game, source);
diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java
index 2443d88abf6..efae23008cd 100644
--- a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java
@@ -30,7 +30,7 @@ package mage.abilities.mana;
import mage.Mana;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.common.AddManaOfAnyColorEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.constants.Zone;
public class AnyColorManaAbility extends ActivatedManaAbilityImpl {
diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java
index b94f573055c..58c544d2ea5 100644
--- a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java
@@ -82,6 +82,18 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ controller.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Mana mana = new Mana();
Mana types = getManaTypes(game, source);
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
@@ -121,11 +133,11 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!player.choose(outcome, choice, game)) {
- return false;
+ return mana;
}
}
if (choice.getChoice() != null) {
- Mana mana = new Mana();
+
switch (choice.getChoice()) {
case "Black":
mana.setBlack(1);
@@ -146,16 +158,9 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
mana.setColorless(1);
break;
}
- checkToFirePossibleEvents(mana, game, source);
- player.getManaPool().addMana(mana, game, source);
}
}
- return true;
- }
-
- @Override
- public Mana getMana(Game game, Ability source) {
- return null;
+ return mana;
}
private Mana getManaTypes(Game game, Ability source) {
@@ -167,19 +172,19 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
return types;
}
inManaTypeCalculation = true;
-
+
ObjectColor permanentColor;
-
+
List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game);
-
+
for (Permanent permanent : permanents) {
permanentColor = permanent.getColor(game);
- if(permanentColor.isColorless())
+ if (permanentColor.isColorless()) {
types.add(Mana.ColorlessMana(1));
- else{
+ } else {
List permanentColors = permanent.getColor(game).getColors();
- for (ObjectColor color : permanentColors){
- types.add(new Mana(color.getColoredManaSymbol()));
+ for (ObjectColor color : permanentColors) {
+ types.add(new Mana(color.getOneColoredManaSymbol()));
}
}
}
@@ -187,6 +192,7 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
return types;
}
+ @Override
public List getNetMana(Game game, Ability source) {
List netManas = new ArrayList<>();
Mana types = getManaTypes(game, source);
diff --git a/Mage/src/main/java/mage/abilities/mana/BlackManaAbility.java b/Mage/src/main/java/mage/abilities/mana/BlackManaAbility.java
index 1c6f4f4648f..213c0b07886 100644
--- a/Mage/src/main/java/mage/abilities/mana/BlackManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/BlackManaAbility.java
@@ -29,7 +29,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.ColoredManaSymbol;
/**
diff --git a/Mage/src/main/java/mage/abilities/mana/BlueManaAbility.java b/Mage/src/main/java/mage/abilities/mana/BlueManaAbility.java
index de55909692b..5be60a91dc2 100644
--- a/Mage/src/main/java/mage/abilities/mana/BlueManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/BlueManaAbility.java
@@ -29,7 +29,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.ColoredManaSymbol;
/**
diff --git a/Mage/src/main/java/mage/abilities/mana/ColorlessManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ColorlessManaAbility.java
index b58d43e1398..0ac8168f1b8 100644
--- a/Mage/src/main/java/mage/abilities/mana/ColorlessManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ColorlessManaAbility.java
@@ -28,7 +28,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
/**
*
diff --git a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
index 0af4d691c6a..0617185ed9e 100644
--- a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
@@ -122,6 +122,18 @@ class CommanderIdentityManaEffect extends ManaEffect {
@Override
public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller != null) {
+ checkToFirePossibleEvents(getMana(game, source), game, source);
+ controller.getManaPool().addMana(getMana(game, source), game, source);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Mana produceMana(boolean netMana, Game game, Ability source) {
+ Mana mana = new Mana();
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Choice choice = new ChoiceImpl();
@@ -152,10 +164,10 @@ class CommanderIdentityManaEffect extends ManaEffect {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!controller.choose(outcome, choice, game)) {
- return false;
+ return mana;
}
}
- Mana mana = new Mana();
+
switch (choice.getChoice()) {
case "Black":
mana.setBlack(1);
@@ -173,16 +185,10 @@ class CommanderIdentityManaEffect extends ManaEffect {
mana.setWhite(1);
break;
}
- checkToFirePossibleEvents(mana, game, source);
- controller.getManaPool().addMana(mana, game, source);
- return true;
+
}
}
- return false;
+ return mana;
}
- @Override
- public Mana getMana(Game game, Ability source) {
- return null;
- }
}
diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java
index 916997a34c3..5a4e93f0944 100644
--- a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java
@@ -33,7 +33,7 @@ import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
-import mage.abilities.effects.common.AddConditionalManaOfAnyColorEffect;
+import mage.abilities.effects.mana.AddConditionalManaOfAnyColorEffect;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.constants.Zone;
import mage.game.Game;
diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java
index df717ecdfa6..c89e16ce1ae 100644
--- a/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ConditionalColoredManaAbility.java
@@ -8,7 +8,7 @@ package mage.abilities.mana;
import mage.Mana;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.common.AddConditionalManaEffect;
+import mage.abilities.effects.mana.AddConditionalManaEffect;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java
index 8bf323b9d33..6c67a7f6579 100644
--- a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java
@@ -8,7 +8,7 @@ package mage.abilities.mana;
import mage.Mana;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.common.AddConditionalColorlessManaEffect;
+import mage.abilities.effects.mana.AddConditionalColorlessManaEffect;
import mage.abilities.mana.builder.ConditionalManaBuilder;
import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java b/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java
index 39ae1331bc1..a79b59192ad 100644
--- a/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/DynamicManaAbility.java
@@ -33,7 +33,7 @@ import mage.Mana;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.dynamicvalue.DynamicValue;
-import mage.abilities.effects.common.DynamicManaEffect;
+import mage.abilities.effects.mana.DynamicManaEffect;
import mage.constants.Zone;
import mage.game.Game;
@@ -111,7 +111,7 @@ public class DynamicManaAbility extends ActivatedManaAbilityImpl {
if (game != null) {
// TODO: effects from replacement effects like Mana Reflection are not considered yet
// TODO: effects that need a X payment (e.g. Mage-Ring Network) return always 0
- newNetMana.add(manaEffect.computeMana(true, game, this));
+ newNetMana.addAll(manaEffect.getNetMana(game, this));
}
return newNetMana;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/GreenManaAbility.java b/Mage/src/main/java/mage/abilities/mana/GreenManaAbility.java
index 54f00eccca8..16e8cb14735 100644
--- a/Mage/src/main/java/mage/abilities/mana/GreenManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/GreenManaAbility.java
@@ -29,7 +29,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.ColoredManaSymbol;
/**
diff --git a/Mage/src/main/java/mage/abilities/mana/RedManaAbility.java b/Mage/src/main/java/mage/abilities/mana/RedManaAbility.java
index b07cc876061..312fe860b1d 100644
--- a/Mage/src/main/java/mage/abilities/mana/RedManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/RedManaAbility.java
@@ -29,7 +29,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.ColoredManaSymbol;
/**
diff --git a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java
index 0163abd7a67..26ff6b599e9 100644
--- a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java
@@ -30,9 +30,8 @@ package mage.abilities.mana;
import java.util.List;
import mage.Mana;
import mage.abilities.costs.Cost;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.BasicManaEffect;
import mage.abilities.effects.common.ManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.Zone;
import mage.game.Game;
@@ -79,15 +78,8 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl {
@Override
public List getNetMana(Game game) {
- if (netMana.isEmpty() && predictable) {
- for (Effect effect : getEffects()) {
- if (effect instanceof ManaEffect) {
- Mana effectMana = ((ManaEffect) effect).getMana(game, this);
- if (effectMana != null) {
- netMana.add(effectMana);
- }
- }
- }
+ if (predictable) {
+ return super.getNetMana(game);
}
return netMana;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java b/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java
index 90b027b03d2..4f676cef9c5 100644
--- a/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/TriggeredManaAbility.java
@@ -32,7 +32,6 @@ import java.util.List;
import mage.Mana;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.DynamicManaEffect;
import mage.abilities.effects.common.ManaEffect;
import mage.constants.AbilityType;
import mage.constants.Zone;
@@ -72,20 +71,14 @@ public abstract class TriggeredManaAbility extends TriggeredAbilityImpl implemen
*/
@Override
public List getNetMana(Game game) {
- if (!getEffects().isEmpty()) {
- Effect effect = getEffects().get(0);
- if (effect != null && game != null) {
- ArrayList newNetMana = new ArrayList<>();
- if (effect instanceof DynamicManaEffect) {
-
- // TODO: effects from replacement effects like Mana Reflection are not considered yet
- // TODO: effects that need a X payment (e.g. Mage-Ring Network) return always 0
- newNetMana.add(((DynamicManaEffect) effect).computeMana(true, game, this));
- } else if (effect instanceof Effect) {
- newNetMana.add(((ManaEffect) effect).getMana(game, this));
+ if (game != null) {
+ ArrayList newNetMana = new ArrayList<>();
+ for (Effect effect : getEffects()) {
+ if (effect instanceof ManaEffect) {
+ newNetMana.addAll(((ManaEffect) effect).getNetMana(game, this));
}
- return newNetMana;
}
+ return newNetMana;
}
return netMana;
}
diff --git a/Mage/src/main/java/mage/abilities/mana/WhiteManaAbility.java b/Mage/src/main/java/mage/abilities/mana/WhiteManaAbility.java
index 600544fea08..5867b21724d 100644
--- a/Mage/src/main/java/mage/abilities/mana/WhiteManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/WhiteManaAbility.java
@@ -29,7 +29,7 @@
package mage.abilities.mana;
import mage.Mana;
-import mage.abilities.effects.common.BasicManaEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
import mage.constants.ColoredManaSymbol;
/**
diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java
index db7017c9ea2..57c4c9427d6 100644
--- a/Mage/src/main/java/mage/cards/CardImpl.java
+++ b/Mage/src/main/java/mage/cards/CardImpl.java
@@ -55,6 +55,7 @@ import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.*;
+import mage.game.command.CommandObject;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
@@ -618,14 +619,18 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
stackObject = game.getStack().getSpell(getId());
}
if (stackObject != null) {
- removed = game.getStack().remove(stackObject);
+ removed = game.getStack().remove(stackObject, game);
lkiObject = stackObject;
}
break;
case COMMAND:
- lkiObject = game.getObject(objectId);
+ for (CommandObject commandObject : game.getState().getCommand()) {
+ if (commandObject.getId().equals(objectId)) {
+ lkiObject = commandObject;
+ }
+ }
if (lkiObject != null) {
- removed = game.getState().getCommand().remove(game.getObject(objectId));
+ removed = game.getState().getCommand().remove((CommandObject) lkiObject);
}
break;
case OUTSIDE:
diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java
index 473021c2751..3174bfdc00f 100644
--- a/Mage/src/main/java/mage/cards/CardsImpl.java
+++ b/Mage/src/main/java/mage/cards/CardsImpl.java
@@ -54,6 +54,12 @@ public class CardsImpl extends LinkedHashSet implements Cards, Serializabl
}
}
+ public CardsImpl(Set cards) {
+ for (Card card : cards) {
+ this.add(card.getId());
+ }
+ }
+
public CardsImpl(Collection cardIds) {
if (cardIds != null) {
this.addAll(cardIds);
diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java
index c1f490e833f..8689428734d 100644
--- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java
+++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java
@@ -110,7 +110,15 @@ public class TxtDeckImporter extends DeckImporter {
}
String lineNum = line.substring(0, delim).trim();
String lineName = line.substring(delim).replace("’", "\'").trim();
- lineName = lineName.replace("&", "//").replace("Æ", "Ae").replace("ö", "ö").replace("û", "u").replace("\"", "'");
+ lineName = lineName
+ .replace("&", "//")
+ .replace("Æ", "Ae")
+ .replace("ö", "o")
+ .replace("û", "u")
+ .replace("í", "i")
+ .replace("â", "a")
+ .replace("á", "a")
+ .replace("\"", "'");
if (lineName.contains("//") && !lineName.contains(" // ")) {
lineName = lineName.replace("//", " // ");
}
diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java
index 9e8aba58084..39201d4a6d9 100644
--- a/Mage/src/main/java/mage/cards/repository/CardRepository.java
+++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java
@@ -58,7 +58,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 51;
// raise this if new cards were added to the server
- private static final long CARD_CONTENT_VERSION = 108;
+ private static final long CARD_CONTENT_VERSION = 109;
private Dao cardDao;
private Set classNames;
diff --git a/Mage/src/main/java/mage/constants/DependencyType.java b/Mage/src/main/java/mage/constants/DependencyType.java
index e5533442bf6..abb7a8fb846 100644
--- a/Mage/src/main/java/mage/constants/DependencyType.java
+++ b/Mage/src/main/java/mage/constants/DependencyType.java
@@ -39,7 +39,6 @@ package mage.constants;
* @author LevelX2
*/
public enum DependencyType {
-
AuraAddingRemoving,
ArtifactAddingRemoving,
AddingAbility,
@@ -48,6 +47,7 @@ public enum DependencyType {
BecomeMountain,
BecomePlains,
BecomeSwamp,
+ BecomeCreature,
EnchantmentAddingRemoving,
LooseDefenderEffect
-}
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/constants/Duration.java b/Mage/src/main/java/mage/constants/Duration.java
index c2d827c989b..e23951b8e12 100644
--- a/Mage/src/main/java/mage/constants/Duration.java
+++ b/Mage/src/main/java/mage/constants/Duration.java
@@ -5,21 +5,23 @@ package mage.constants;
* @author North
*/
public enum Duration {
- OneUse(""),
- EndOfGame("for the rest of the game"),
- WhileOnBattlefield(""),
- WhileOnStack(""),
- WhileInGraveyard(""),
- EndOfTurn("until end of turn"),
- UntilYourNextTurn("until your next turn"),
- EndOfCombat("until end of combat"),
- EndOfStep("until end of phase step"),
- Custom("");
+ OneUse("", true),
+ EndOfGame("for the rest of the game", false),
+ WhileOnBattlefield("", false),
+ WhileOnStack("", false),
+ WhileInGraveyard("", false),
+ EndOfTurn("until end of turn", true),
+ UntilYourNextTurn("until your next turn", true),
+ EndOfCombat("until end of combat", true),
+ EndOfStep("until end of phase step", true),
+ Custom("", true);
private final String text;
+ private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not chnaged zone since init of the effect
- Duration(String text) {
+ Duration(String text, boolean onlyValidIfNoZoneChange) {
this.text = text;
+ this.onlyValidIfNoZoneChange = onlyValidIfNoZoneChange;
}
@Override
@@ -27,4 +29,8 @@ public enum Duration {
return text;
}
+ public boolean isOnlyValidIfNoZoneChange() {
+ return onlyValidIfNoZoneChange;
+ }
+
}
diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java
index c634327fae9..33938432a46 100644
--- a/Mage/src/main/java/mage/constants/SubType.java
+++ b/Mage/src/main/java/mage/constants/SubType.java
@@ -4,6 +4,7 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
+
import mage.util.SubTypeList;
public enum SubType {
@@ -328,17 +329,16 @@ public enum SubType {
TROOPER("Trooper", SubTypeSet.CreatureType, true), // Star Wars
TRILOBITE("Trilobite", SubTypeSet.CreatureType),
TWILEK("Twi'lek", SubTypeSet.CreatureType, true), // Star Wars
-
// U
UGNAUGHT("Ugnaught", SubTypeSet.CreatureType, true),
UNICORN("Unicorn", SubTypeSet.CreatureType),
- //V
+ // V
VAMPIRE("Vampire", SubTypeSet.CreatureType),
VEDALKEN("Vedalken", SubTypeSet.CreatureType),
VIASHINO("Viashino", SubTypeSet.CreatureType),
VILLAIN("Villain", SubTypeSet.CreatureType, true), // Unstable
VOLVER("Volver", SubTypeSet.CreatureType),
- //W
+ // W
WALL("Wall", SubTypeSet.CreatureType),
WARRIOR("Warrior", SubTypeSet.CreatureType),
WEEQUAY("Weequay", SubTypeSet.CreatureType, true),
@@ -429,6 +429,16 @@ public enum SubType {
return description;
}
+ public static SubType fromString(String value) {
+ for (SubType st : SubType.values()) {
+ if (st.toString().equals(value)) {
+ return st;
+ }
+ }
+
+ throw new IllegalArgumentException("Can''t find subtype enum value: " + value);
+ }
+
public static SubType byDescription(String subType) {
for (SubType s : values()) {
if (s.getDescription().equals(subType)) {
diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java
index c7ecd539e57..39e6d55ef72 100644
--- a/Mage/src/main/java/mage/counters/CounterType.java
+++ b/Mage/src/main/java/mage/counters/CounterType.java
@@ -39,6 +39,7 @@ public enum CounterType {
ARROWHEAD("arrowhead"),
AWAKENING("awakening"),
BLAZE("blaze"),
+ BLOOD("blood"),
BOUNTY("bounty"),
BRIBERY("bribery"),
BRICK("brick"),
@@ -67,10 +68,12 @@ public enum CounterType {
FADE("fade"),
FATE("fate"),
FEATHER("feather"),
+ FILIBUSTER("filibuster"),
FLOOD("flood"),
FURY("fury"),
FUNGUS("fungus"),
FUSE("fuse"),
+ GEM("gem"),
GLOBE("globe"),
GOLD("gold"),
GROWTH("growth"),
@@ -83,6 +86,7 @@ public enum CounterType {
ICE("ice"),
INFECTION("infection"),
INTERVENTION("intervention"),
+ ISOLATION("isolation"),
JAVELIN("javelin"),
KI("ki"),
LANDMARK("landmark"),
@@ -90,6 +94,7 @@ public enum CounterType {
LORE("lore"),
LUCK("luck"),
LOYALTY("loyalty"),
+ MANIFESTATION("manifestation"),
MANNEQUIN("mannequin"),
M1M1(new BoostCounter(-1, -1).name),
M2M1(new BoostCounter(-2, -1).name),
@@ -99,6 +104,7 @@ public enum CounterType {
MIRE("mire"),
MUSTER("muster"),
NET("net"),
+ OMEN("omen"),
P0P1(new BoostCounter(0, 1).name),
P1P0(new BoostCounter(1, 0).name),
P1P1(new BoostCounter(1, 1).name),
@@ -108,6 +114,7 @@ public enum CounterType {
PAIN("pain"),
PETAL("petal"),
PETRIFICATION("petrification"),
+ PHYLACTERY("phylactery"),
PLAGUE("plague"),
PLOT("plot"),
POLYP("polyp"),
@@ -123,6 +130,7 @@ public enum CounterType {
SHIELD("shield"),
SHRED("shred"),
SLIME("slime"),
+ SOOT("soot"),
SPITE("spite"),
SPORE("spore"),
STORAGE("storage"),
@@ -132,6 +140,7 @@ public enum CounterType {
TIDE("tide"),
TIME("time"),
TOWER("tower"),
+ TRAINING("training"),
TRAP("trap"),
TREASURE("treasure"),
UNITY("unity"),
diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java
index 5e0dee3d5c5..7ef7ed806b9 100644
--- a/Mage/src/main/java/mage/filter/StaticFilters.java
+++ b/Mage/src/main/java/mage/filter/StaticFilters.java
@@ -12,6 +12,7 @@ import mage.constants.TargetController;
import mage.filter.common.*;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
+import mage.filter.predicate.mageobject.MulticoloredPredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
@@ -75,6 +76,13 @@ public final class StaticFilters {
static {
FILTER_CARD_CREATURE_YOUR_GRAVEYARD.setLockedFilter(true);
}
+
+ public static final FilterCard FILTER_CARD_FROM_YOUR_GRAVEYARD = new FilterCard("card from your graveyard");
+
+ static {
+ FILTER_CARD_FROM_YOUR_GRAVEYARD.setLockedFilter(true);
+ }
+
public static final FilterLandCard FILTER_CARD_LAND = new FilterLandCard();
static {
@@ -102,6 +110,13 @@ public final class StaticFilters {
static {
FILTER_PERMANENT.setLockedFilter(true);
}
+
+ public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_AN = new FilterArtifactPermanent("an artifact");
+
+ static {
+ FILTER_PERMANENT_ARTIFACT_AN.setLockedFilter(true);
+ }
+
public static final FilterArtifactOrEnchantmentPermanent FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT = new FilterArtifactOrEnchantmentPermanent();
static {
@@ -363,10 +378,18 @@ public final class StaticFilters {
static {
FILTER_SPELL.setLockedFilter(true);
}
- public static final FilterSpell FILTER_A_SPELL = new FilterSpell("a spell");
+
+ public static final FilterSpell FILTER_SPELL_A = new FilterSpell("a spell");
static {
- FILTER_A_SPELL.setLockedFilter(true);
+ FILTER_SPELL_A.setLockedFilter(true);
+ }
+
+ public static final FilterSpell FILTER_SPELL_A_MULTICOLORED = new FilterSpell("a multicolored spell");
+
+ static {
+ FILTER_SPELL_A_MULTICOLORED.add(new MulticoloredPredicate());
+ FILTER_SPELL_A_MULTICOLORED.setLockedFilter(true);
}
public static final FilterSpell FILTER_INSTANT_OR_SORCERY_SPELL = new FilterSpell("instant or sorcery spell");
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 0149ae16909..bd5e17cc79c 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -328,12 +328,15 @@ public abstract class GameImpl implements Game, Serializable {
MageObject object;
if (state.getBattlefield().containsPermanent(objectId)) {
object = state.getBattlefield().getPermanent(objectId);
- state.setZone(objectId, Zone.BATTLEFIELD); // why is this neccessary?
+ // state.setZone(objectId, Zone.BATTLEFIELD); // why is this neccessary?
return object;
}
+ if (getPermanentsEntering().containsKey(objectId)) {
+ return getPermanentEntering(objectId);
+ }
for (StackObject item : state.getStack()) {
if (item.getId().equals(objectId)) {
- state.setZone(objectId, Zone.STACK); // why is this neccessary?
+ // state.setZone(objectId, Zone.STACK); // why is this neccessary?
return item;
}
if (item.getSourceId().equals(objectId) && item instanceof Spell) {
@@ -1383,7 +1386,7 @@ public abstract class GameImpl implements Game, Serializable {
} catch (Exception ex) {
logger.fatal("Game exception gameId: " + getId(), ex);
if ((ex instanceof NullPointerException)
- && errorContinueCounter == 1 && ex.getStackTrace() != null) {
+ && errorContinueCounter == 0 && ex.getStackTrace() != null) {
logger.fatal(ex.getStackTrace());
}
this.fireErrorEvent("Game exception occurred: ", ex);
@@ -1421,7 +1424,7 @@ public abstract class GameImpl implements Game, Serializable {
top.resolve(this);
} finally {
if (top != null) {
- state.getStack().remove(top); // seems partly redundant because move card from stack to grave is already done and the stack removed
+ state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
rememberLKI(top.getSourceId(), Zone.STACK, top);
checkInfiniteLoop(top.getSourceId());
if (!getTurn().isEndTurnRequested()) {
@@ -2581,10 +2584,10 @@ public abstract class GameImpl implements Game, Serializable {
it.remove();
}
}
-
+
if (addPlaneAgain) {
boolean addedAgain = false;
- for (Player aplayer : state.getPlayers().values()) {
+ for (Player aplayer : state.getPlayers().values()) {
if (!aplayer.hasLeft() && !addedAgain) {
addedAgain = true;
Plane plane = Plane.getRandomPlane();
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index 994f62c4a95..2e59a19dc01 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -30,7 +30,6 @@ package mage.game;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
-
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.effects.ContinuousEffect;
@@ -121,6 +120,8 @@ public class GameState implements Serializable, Copyable {
private Map copiedCards = new HashMap<>();
private int permanentOrderNumber;
+ private int applyEffectsCounter; // Upcounting number of each applyEffects execution
+
public GameState() {
players = new Players();
playerList = new PlayerList();
@@ -137,6 +138,7 @@ public class GameState implements Serializable, Copyable {
combat = new Combat();
turnMods = new TurnMods();
watchers = new Watchers();
+ applyEffectsCounter = 0;
}
public GameState(final GameState state) {
@@ -193,6 +195,7 @@ public class GameState implements Serializable, Copyable {
this.zoneChangeCounter.putAll(state.zoneChangeCounter);
this.copiedCards.putAll(state.copiedCards);
this.permanentOrderNumber = state.permanentOrderNumber;
+ this.applyEffectsCounter = state.applyEffectsCounter;
}
public void restoreForRollBack(GameState state) {
@@ -210,7 +213,7 @@ public class GameState implements Serializable, Copyable {
this.command = state.command;
this.isPlaneChase = state.isPlaneChase;
this.seenPlanes = state.seenPlanes;
- this.designations = state.designations;
+ this.designations = state.designations;
this.exile = state.exile;
this.battlefield = state.battlefield;
this.turnNum = state.turnNum;
@@ -237,6 +240,7 @@ public class GameState implements Serializable, Copyable {
this.zoneChangeCounter = state.zoneChangeCounter;
this.copiedCards = state.copiedCards;
this.permanentOrderNumber = state.permanentOrderNumber;
+ this.applyEffectsCounter = state.applyEffectsCounter;
}
@Override
@@ -466,12 +470,12 @@ public class GameState implements Serializable, Copyable {
}
}
return null;
- }
-
+ }
+
public List getSeenPlanes() {
return seenPlanes;
}
-
+
public boolean isPlaneChase() {
return isPlaneChase;
}
@@ -574,6 +578,7 @@ public class GameState implements Serializable, Copyable {
}
public void applyEffects(Game game) {
+ applyEffectsCounter++;
for (Player player : players.values()) {
player.reset();
}
@@ -680,7 +685,11 @@ public class GameState implements Serializable, Copyable {
}
public void setZone(UUID id, Zone zone) {
- zones.put(id, zone);
+ if (zone == null) {
+ zones.remove(id);
+ } else {
+ zones.put(id, zone);
+ }
}
public void addSimultaneousEvent(GameEvent event, Game game) {
@@ -752,8 +761,8 @@ public class GameState implements Serializable, Copyable {
ZoneChangeData data = (ZoneChangeData) obj;
return this.fromZone == data.fromZone
&& this.toZone == data.toZone
- && this.sourceId == data.sourceId
- && this.playerId == data.playerId;
+ && Objects.equals(this.sourceId, data.sourceId)
+ && Objects.equals(this.playerId, data.playerId);
}
return false;
}
@@ -881,13 +890,13 @@ public class GameState implements Serializable, Copyable {
addAbility(ability, designation.getId(), null);
}
}
-
+
public void addSeenPlane(Plane plane, Game game, UUID controllerId) {
if (plane != null) {
getSeenPlanes().add(plane.getName());
}
}
-
+
public void resetSeenPlanes() {
getSeenPlanes().clear();
}
@@ -1169,4 +1178,9 @@ public class GameState implements Serializable, Copyable {
public int getNextPermanentOrderNumber() {
return permanentOrderNumber++;
}
+
+ public int getApplyEffectsCounter() {
+ return applyEffectsCounter;
+ }
+
}
diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java
index 9a145c862dc..c14b2da8842 100644
--- a/Mage/src/main/java/mage/game/ZonesHandler.java
+++ b/Mage/src/main/java/mage/game/ZonesHandler.java
@@ -134,12 +134,15 @@ public final class ZonesHandler {
case STACK:
// There should never be more than one card here.
for (Card card : cards.getCards(game)) {
+ Spell spell;
if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) {
- game.getStack().push(((ZoneChangeInfo.Stack) info).spell);
+ spell = ((ZoneChangeInfo.Stack) info).spell;
} else {
- game.getStack().push(
- new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone()));
+ spell = new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone());
}
+ game.getStack().push(spell);
+ game.getState().setZone(spell.getId(), Zone.STACK);
+ game.getState().setZone(card.getId(), Zone.STACK);
}
break;
case BATTLEFIELD:
diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java
index c66dcd0c06d..e1f003e1d87 100644
--- a/Mage/src/main/java/mage/game/combat/CombatGroup.java
+++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java
@@ -28,11 +28,8 @@
package mage.game.combat;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
+
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
import mage.abilities.common.ControllerDivideCombatDamageAbility;
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
@@ -286,7 +283,7 @@ public class CombatGroup implements Serializable, Copyable {
if (attacker == null) {
return;
}
- boolean oldRuleDamage = (player.getId() == defendingPlayerId);
+ boolean oldRuleDamage = (Objects.equals(player.getId(), defendingPlayerId));
int damage = getDamageValueFromPermanent(attacker, game);
if (canDamage(attacker, first)) {
// must be set before attacker damage marking because of effects like Test of Faith
diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
index 9caa2b5677e..23146f69957 100644
--- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
@@ -29,6 +29,7 @@ package mage.game.command.planes;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.ActivateIfConditionActivatedAbility;
@@ -132,7 +133,7 @@ class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl {
}
}
Permanent permanent = game.getPermanent(event.getTargetId());
- if (permanent != null && filter.match(permanent, game) && permanent.getControllerId() == game.getActivePlayerId()) {
+ if (permanent != null && filter.match(permanent, game) && Objects.equals(permanent.getControllerId(), game.getActivePlayerId())) {
UUID oldController = source.getControllerId();
source.setControllerId(game.getActivePlayerId());
Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(2));
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java
index c005bef4294..ba625cc5582 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java
@@ -46,7 +46,7 @@ public class PermanentToken extends PermanentImpl {
super(controllerId, controllerId, token.getName());
this.expansionSetCode = expansionSetCode;
this.token = token.copy();
- this.token.getAbilities().newId(); // neccessary if token has ability like DevourAbility()
+ this.token.getAbilities().newOriginalId(); // neccessary if token has ability like DevourAbility()
this.token.getAbilities().setSourceId(objectId);
this.power.modifyBaseValue(token.getPower().getBaseValueModified());
this.toughness.modifyBaseValue(token.getToughness().getBaseValueModified());
@@ -84,7 +84,8 @@ public class PermanentToken extends PermanentImpl {
for (ManaCost cost : token.getManaCost()) {
this.getManaCost().add(cost.copy());
}
- this.cardType = token.getCardType();
+ this.cardType.clear();
+ this.cardType.addAll(token.getCardType());
this.color = token.getColor(game).copy();
this.frameColor = token.getFrameColor(game);
this.frameStyle = token.getFrameStyle();
diff --git a/Mage/src/main/java/mage/game/permanent/token/BatToken.java b/Mage/src/main/java/mage/game/permanent/token/BatToken.java
index a62ecd7c7bc..68059eb78ff 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BatToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BatToken.java
@@ -1,14 +1,24 @@
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.MageInt;
import mage.abilities.keyword.FlyingAbility;
import mage.constants.CardType;
import mage.constants.SubType;
public class BatToken extends TokenImpl {
+
+ final static private List tokenImageSets = new ArrayList<>();
+
+ static {
+ tokenImageSets.addAll(Arrays.asList("MMA", "C17"));
+ }
public BatToken() {
super("Bat", "1/1 black Bat creature token with flying");
+ availableImageSetCodes = tokenImageSets;
cardType.add(CardType.CREATURE);
color.setBlack(true);
subtype.add(SubType.BAT);
diff --git a/Mage/src/main/java/mage/game/permanent/token/BelzenlokClericToken.java b/Mage/src/main/java/mage/game/permanent/token/BelzenlokClericToken.java
index f6b6a787eef..00eee821106 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BelzenlokClericToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BelzenlokClericToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.MageInt;
import mage.constants.CardType;
import mage.constants.SubType;
@@ -37,8 +40,14 @@ import mage.constants.SubType;
*/
public class BelzenlokClericToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public BelzenlokClericToken() {
super("Cleric", "0/1 black Cleric creature token");
+ availableImageSetCodes = tokenImageSets;
cardType.add(CardType.CREATURE);
subtype.add(SubType.CLERIC);
color.setBlack(true);
diff --git a/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java b/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java
index c2b128189be..e7d194041d9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BelzenlokDemonToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
@@ -51,9 +54,15 @@ import mage.game.permanent.Permanent;
*/
public class BelzenlokDemonToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public BelzenlokDemonToken() {
super("Demon", "6/6 black Demon creature token with flying, trample, and "
+ "\"At the beginning of your upkeep, sacrifice another creature. If you can't, this creature deals 6 damage to you.\"");
+ availableImageSetCodes = tokenImageSets;
cardType.add(CardType.CREATURE);
color.setBlack(true);
subtype.add(SubType.DEMON);
diff --git a/Mage/src/main/java/mage/game/permanent/token/CatToken.java b/Mage/src/main/java/mage/game/permanent/token/CatToken.java
index 52c5ff44b79..5af06d60428 100644
--- a/Mage/src/main/java/mage/game/permanent/token/CatToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/CatToken.java
@@ -44,7 +44,7 @@ public class CatToken extends TokenImpl {
final static private List tokenImageSets = new ArrayList<>();
static {
- tokenImageSets.addAll(Arrays.asList("SOM", "M13", "M14", "C14", "C15"));
+ tokenImageSets.addAll(Arrays.asList("SOM", "M13", "M14", "C14", "C15", "C17"));
}
public CatToken() {
diff --git a/Mage/src/main/java/mage/game/permanent/token/CatWarriorToken.java b/Mage/src/main/java/mage/game/permanent/token/CatWarriorToken.java
index 147b1ab6206..0e526afe6ac 100644
--- a/Mage/src/main/java/mage/game/permanent/token/CatWarriorToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/CatWarriorToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.abilities.keyword.ForestwalkAbility;
import mage.constants.SubType;
@@ -37,8 +40,15 @@ import mage.constants.SubType;
*/
public class CatWarriorToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+
+ static {
+ tokenImageSets.addAll(Arrays.asList("PLC", "C17"));
+ }
+
public CatWarriorToken() {
super("Cat Warrior", "2/2 green Cat Warrior creature token with forestwalk");
+ availableImageSetCodes = tokenImageSets;
this.setOriginalExpansionSetCode("PLC");
this.getPower().modifyBaseValue(2);
this.getToughness().modifyBaseValue(2);
diff --git a/Mage/src/main/java/mage/game/permanent/token/ChainersTormentNightmareToken.java b/Mage/src/main/java/mage/game/permanent/token/ChainersTormentNightmareToken.java
index db1d907981b..c4042149cf9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ChainersTormentNightmareToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ChainersTormentNightmareToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
@@ -37,10 +40,23 @@ import mage.MageInt;
*/
public class ChainersTormentNightmareToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
+ public ChainersTormentNightmareToken() { this(0); };
+
public ChainersTormentNightmareToken(int xValue) {
- super("Nightmare", "X/X black Nightmare artifact creature token");
+ super("Nightmare Horror", "X/X black Nightmare Horror creature token");
+
+ availableImageSetCodes = tokenImageSets;
+ setOriginalExpansionSetCode("DOM");
+
cardType.add(CardType.CREATURE);
subtype.add(SubType.NIGHTMARE);
+ subtype.add(SubType.HORROR);
+ color.setBlack(true);
power = new MageInt(xValue);
toughness = new MageInt(xValue);
}
diff --git a/Mage/src/main/java/mage/game/permanent/token/DeathtouchRatToken.java b/Mage/src/main/java/mage/game/permanent/token/DeathtouchRatToken.java
index 91cf8cf3d79..385cf063456 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DeathtouchRatToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DeathtouchRatToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.MageInt;
import mage.abilities.keyword.DeathtouchAbility;
@@ -37,10 +40,16 @@ import mage.constants.SubType;
* @author Saga
*/
public class DeathtouchRatToken extends TokenImpl {
+
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("C17"));
+ }
public DeathtouchRatToken() {
super("Rat", "1/1 black Rat creature token with deathtouch");
this.setExpansionSetCodeForImage("C17");
+ availableImageSetCodes = tokenImageSets;
this.cardType.add(CardType.CREATURE);
this.color.setBlack(true);
this.subtype.add(SubType.RAT);
diff --git a/Mage/src/main/java/mage/game/permanent/token/DragonToken.java b/Mage/src/main/java/mage/game/permanent/token/DragonToken.java
index 125243d346b..9d6473f30be 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DragonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DragonToken.java
@@ -45,7 +45,7 @@ public class DragonToken extends TokenImpl {
final static private List tokenImageSets = new ArrayList<>();
static {
- tokenImageSets.addAll(Arrays.asList("DTK", "MMA", "ALA", "MM3"));
+ tokenImageSets.addAll(Arrays.asList("DTK", "MMA", "ALA", "MM3", "C17"));
}
public DragonToken() {
diff --git a/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovToken.java b/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovToken.java
index fb84b3ddac0..ac8b587e67f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
@@ -37,8 +40,15 @@ import mage.MageInt;
*/
public class EdgarMarkovToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("C17"));
+ }
+
public EdgarMarkovToken() {
super("Vampire", "1/1 black Vampire creature token");
+ availableImageSetCodes = tokenImageSets;
+ setExpansionSetCodeForImage("C17");
cardType.add(CardType.CREATURE);
color.setBlack(true);
subtype.add(SubType.VAMPIRE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java b/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java
index 0060d393369..9cb67ede915 100644
--- a/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java
@@ -49,7 +49,7 @@ public class EldraziSpawnToken extends TokenImpl {
final static private List tokenImageSets = new ArrayList<>();
static {
- tokenImageSets.addAll(Arrays.asList("ROE", "MM2", "DDP"));
+ tokenImageSets.addAll(Arrays.asList("ROE", "MM2", "DDP", "C17"));
}
public EldraziSpawnToken() {
@@ -62,8 +62,8 @@ public class EldraziSpawnToken extends TokenImpl {
addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new SacrificeSourceCost()));
availableImageSetCodes = tokenImageSets;
- // Get one of the three possible token images
- this.setTokenType(RandomUtil.nextInt(3) + 1);
+ // Get one of the four possible token images
+ this.setTokenType(RandomUtil.nextInt(4) + 1);
}
public EldraziSpawnToken(final EldraziSpawnToken token) {
@@ -73,4 +73,4 @@ public class EldraziSpawnToken extends TokenImpl {
public EldraziSpawnToken copy() {
return new EldraziSpawnToken(this);
}
-}
\ No newline at end of file
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java b/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java
index ce21470e9a0..af50da7442b 100644
--- a/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java
@@ -31,7 +31,7 @@ import mage.constants.CardType;
import mage.abilities.Ability;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.common.AddManaOfAnyColorEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.abilities.mana.SimpleManaAbility;
import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/game/permanent/token/GeminiEngineTwinToken.java b/Mage/src/main/java/mage/game/permanent/token/GeminiEngineTwinToken.java
index 951b7f611fd..0ad9c13fbf2 100644
--- a/Mage/src/main/java/mage/game/permanent/token/GeminiEngineTwinToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GeminiEngineTwinToken.java
@@ -6,6 +6,8 @@ import mage.constants.SubType;
public class GeminiEngineTwinToken extends TokenImpl {
+ public GeminiEngineTwinToken() { this(0, 0); }
+
public GeminiEngineTwinToken(int power, int toughness) {
super("Twin", "colorless Construct artifact creature token named Twin that's attacking. Its power is equal to Gemini Engine's power and its toughness is equal to Gemini Engine's toughness.");
cardType.add(CardType.ARTIFACT);
diff --git a/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java
index 52f5a2a3bd1..6134019de3e 100644
--- a/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GoblinToken.java
@@ -44,7 +44,8 @@ public class GoblinToken extends TokenImpl {
static {
tokenImageSets.addAll(Arrays.asList("10E", "ALA", "SOM", "M10", "NPH", "M13", "RTR",
- "MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "DD3EVG", "MM2", "MM3", "EMA", "C16"));
+ "MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "DD3EVG", "MM2",
+ "MM3", "EMA", "C16", "DOM"));
}
public GoblinToken() {
diff --git a/Mage/src/main/java/mage/game/permanent/token/GoldToken.java b/Mage/src/main/java/mage/game/permanent/token/GoldToken.java
index bdc6286180f..b23df81cbaf 100644
--- a/Mage/src/main/java/mage/game/permanent/token/GoldToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GoldToken.java
@@ -32,7 +32,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import mage.abilities.costs.common.SacrificeSourceCost;
-import mage.abilities.effects.common.AddManaOfAnyColorEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.abilities.mana.SimpleManaAbility;
import mage.constants.CardType;
import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
index a4a3aaee441..8c265f1de33 100644
--- a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import static javax.management.Query.value;
import mage.constants.CardType;
import mage.constants.SubType;
@@ -52,12 +55,18 @@ public class KarnConstructToken extends TokenImpl {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
}
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public KarnConstructToken() {
this("DOM");
}
public KarnConstructToken(String setCode) {
super("Construct", "0/0 colorless Construct artifact creature token with \"This creature gets +1/+1 for each artifact you control.\"");
+ availableImageSetCodes = tokenImageSets;
this.setOriginalExpansionSetCode(setCode);
cardType.add(CardType.ARTIFACT);
cardType.add(CardType.CREATURE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/KaroxBladewingDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/KaroxBladewingDragonToken.java
index 5830be0b35d..cd212caa88f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/KaroxBladewingDragonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/KaroxBladewingDragonToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.abilities.keyword.FlyingAbility;
import mage.constants.CardType;
import mage.constants.SubType;
@@ -38,14 +41,21 @@ import mage.constants.SuperType;
*/
public class KaroxBladewingDragonToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public KaroxBladewingDragonToken() {
super("Karox Bladewing", "legendary 4/4 red Dragon creature token with flying", 4, 4);
+ availableImageSetCodes = tokenImageSets;
this.setOriginalExpansionSetCode("DOM");
this.addSuperType(SuperType.LEGENDARY);
this.cardType.add(CardType.CREATURE);
this.subtype.add(SubType.DRAGON);
+ this.color.setRed(true);
this.addAbility(FlyingAbility.getInstance());
}
diff --git a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java
index 4e716341372..5ad8c1f7237 100644
--- a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java
@@ -7,6 +7,7 @@ import mage.MageInt;
import mage.abilities.keyword.VigilanceAbility;
import mage.constants.CardType;
import mage.constants.SubType;
+import mage.util.RandomUtil;
/**
*
@@ -25,6 +26,9 @@ public class KnightToken extends TokenImpl {
if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C15")) {
setTokenType(2);
}
+ if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) {
+ this.setTokenType(RandomUtil.nextInt(2) + 1);
+ }
cardType.add(CardType.CREATURE);
color.setWhite(true);
subtype.add(SubType.KNIGHT);
diff --git a/Mage/src/main/java/mage/game/permanent/token/RiptideReplicatorToken.java b/Mage/src/main/java/mage/game/permanent/token/RiptideReplicatorToken.java
index 3cbad8b40b3..f9a49d222d4 100644
--- a/Mage/src/main/java/mage/game/permanent/token/RiptideReplicatorToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/RiptideReplicatorToken.java
@@ -42,7 +42,7 @@ public class RiptideReplicatorToken extends TokenImpl {
this(null, null, 1);
}
public RiptideReplicatorToken(ObjectColor color, SubType type, int x) {
- super(type.getDescription(), "X/X creature token of the chosen color and type");
+ super(type != null ? type.getDescription() : "", "X/X creature token of the chosen color and type");
cardType.add(CardType.CREATURE);
if (color != null) {
this.color.setColor(color);
diff --git a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
index 8837db22f47..ecb3a41b2c0 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
@@ -64,7 +64,9 @@ public class SaprolingToken extends TokenImpl {
"CMA",
"VMA", // 2 different token, one with DIFFERENT stats, "Saproling Burst" create different token, see https://scryfall.com/card/tvma/12
"E02",
- "RIX"));
+ "RIX",
+ "DOM" // 3 different token images
+ ));
}
public SaprolingToken() {
@@ -82,6 +84,9 @@ public class SaprolingToken extends TokenImpl {
if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) {
this.setTokenType(RandomUtil.nextInt(2) + 1);
}
+ if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) {
+ this.setTokenType(RandomUtil.nextInt(3) + 1);
+ }
cardType.add(CardType.CREATURE);
color.setGreen(true);
subtype.add(SubType.SAPROLING);
diff --git a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
index 5960e940258..33e18b779c9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
@@ -45,7 +45,7 @@ public class SoldierToken extends TokenImpl {
static {
tokenImageSets.addAll(Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR",
- "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01"));
+ "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM"));
}
public SoldierToken() {
diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
index b817f3f714a..a15d39ae9a4 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
@@ -33,11 +33,8 @@ import java.util.Locale;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectImpl;
-import mage.ObjectColor;
-import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.cards.Card;
-import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
@@ -47,7 +44,6 @@ import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.util.RandomUtil;
-import mage.util.SubTypeList;
public abstract class TokenImpl extends MageObjectImpl implements Token {
@@ -112,49 +108,56 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
this.tokenDescriptor = tokenDescriptor();
}
+ @Override
public String getTokenDescriptor() {
this.tokenDescriptor = tokenDescriptor();
return tokenDescriptor;
}
private String tokenDescriptor() {
- String name = this.name.replaceAll("[^a-zA-Z0-9]", "");
- String color = this.color.toString().replaceAll("[^a-zA-Z0-9]", "");
- String subtype = this.subtype.toString().replaceAll("[^a-zA-Z0-9]", "");
- String cardType = this.cardType.toString().replaceAll("[^a-zA-Z0-9]", "");
- String originalset = this.getOriginalExpansionSetCode();
- String descriptor = name + '.' + color + '.' + subtype + '.' + cardType + '.' + this.power + '.' + this.toughness;
+ String strName = this.name.replaceAll("[^a-zA-Z0-9]", "");
+ String strColor = this.color.toString().replaceAll("[^a-zA-Z0-9]", "");
+ String strSubtype = this.subtype.toString().replaceAll("[^a-zA-Z0-9]", "");
+ String strCardType = this.cardType.toString().replaceAll("[^a-zA-Z0-9]", "");
+ String descriptor = strName + '.' + strColor + '.' + strSubtype + '.' + strCardType + '.' + this.power + '.' + this.toughness;
descriptor = descriptor.toUpperCase(Locale.ENGLISH);
return descriptor;
}
+ @Override
public String getDescription() {
return description;
}
+ @Override
public UUID getLastAddedToken() {
return lastAddedTokenId;
}
+ @Override
public ArrayList getLastAddedTokenIds() {
ArrayList ids = new ArrayList<>();
ids.addAll(lastAddedTokenIds);
return ids;
}
+ @Override
public void addAbility(Ability ability) {
ability.setSourceId(this.getId());
abilities.add(ability);
}
+ @Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId) {
return this.putOntoBattlefield(amount, game, sourceId, controllerId, false, false);
}
+ @Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking) {
return putOntoBattlefield(amount, game, sourceId, controllerId, tapped, attacking, null);
}
+ @Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer) {
Player controller = game.getPlayer(controllerId);
if (controller == null) {
@@ -228,49 +231,60 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
return false;
}
+ @Override
public void setPower(int power) {
this.power.setValue(power);
}
+ @Override
public void setToughness(int toughness) {
this.toughness.setValue(toughness);
}
+ @Override
public int getTokenType() {
return tokenType;
}
+ @Override
public void setTokenType(int tokenType) {
this.tokenType = tokenType;
}
+ @Override
public String getOriginalCardNumber() {
return originalCardNumber;
}
+ @Override
public void setOriginalCardNumber(String originalCardNumber) {
this.originalCardNumber = originalCardNumber;
}
+ @Override
public String getOriginalExpansionSetCode() {
return originalExpansionSetCode;
}
+ @Override
public void setOriginalExpansionSetCode(String originalExpansionSetCode) {
this.originalExpansionSetCode = originalExpansionSetCode;
setTokenDescriptor();
}
+ @Override
public Card getCopySourceCard() {
return copySourceCard;
}
+ @Override
public void setCopySourceCard(Card copySourceCard) {
if (copySourceCard != null) {
this.copySourceCard = copySourceCard.copy();
}
}
+ @Override
public void setExpansionSetCodeForImage(String code) {
if (!availableImageSetCodes.isEmpty()) {
if (availableImageSetCodes.contains(code)) {
@@ -288,6 +302,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
setTokenDescriptor();
}
+ @Override
public boolean updateExpansionSetCode(String setCode) {
if (setCode == null || setCode.isEmpty()) {
return false;
diff --git a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
index 6f931f910ce..1faade54396 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
@@ -33,7 +33,7 @@ import java.util.List;
import mage.abilities.Ability;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.common.AddManaOfAnyColorEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.abilities.mana.SimpleManaAbility;
import mage.constants.CardType;
import mage.constants.SubType;
diff --git a/Mage/src/main/java/mage/game/permanent/token/UtvaraHellkiteDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/UtvaraHellkiteDragonToken.java
index b2a74140ff4..73c4fe7a90b 100644
--- a/Mage/src/main/java/mage/game/permanent/token/UtvaraHellkiteDragonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/UtvaraHellkiteDragonToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
@@ -37,9 +40,16 @@ import mage.abilities.keyword.FlyingAbility;
* @author spjspj
*/
public class UtvaraHellkiteDragonToken extends TokenImpl {
+
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("C17"));
+ }
public UtvaraHellkiteDragonToken() {
super("Dragon", "6/6 red Dragon creature token with flying");
+ availableImageSetCodes = tokenImageSets;
+ setExpansionSetCodeForImage("C17");
cardType.add(CardType.CREATURE);
color.setRed(true);
subtype.add(SubType.DRAGON);
diff --git a/Mage/src/main/java/mage/game/permanent/token/ValdukElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ValdukElementalToken.java
index 193c8a999af..364ecc45a1c 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ValdukElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ValdukElementalToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
@@ -39,12 +42,18 @@ import mage.abilities.keyword.TrampleAbility;
*/
public class ValdukElementalToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public ValdukElementalToken() {
this("DOM");
}
public ValdukElementalToken(String setCode) {
super("Elemental", "3/1 red Elemental creature token with trample and haste");
+ availableImageSetCodes = tokenImageSets;
cardType.add(CardType.CREATURE);
color.setRed(true);
subtype.add(SubType.ELEMENTAL);
diff --git a/Mage/src/main/java/mage/game/permanent/token/VolrathsLaboratoryToken.java b/Mage/src/main/java/mage/game/permanent/token/VolrathsLaboratoryToken.java
index 063bfceef63..0a4742ba19c 100644
--- a/Mage/src/main/java/mage/game/permanent/token/VolrathsLaboratoryToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/VolrathsLaboratoryToken.java
@@ -42,7 +42,7 @@ public class VolrathsLaboratoryToken extends TokenImpl {
this(null, null);
}
public VolrathsLaboratoryToken(ObjectColor color, SubType type) {
- super(type.getDescription(), "2/2 creature token of the chosen color and type");
+ super(type != null ? type.getDescription() : "", "2/2 creature token of the chosen color and type");
cardType.add(CardType.CREATURE);
if (color != null) {
this.color.setColor(color);
diff --git a/Mage/src/main/java/mage/game/permanent/token/WasitoraCatDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/WasitoraCatDragonToken.java
index eeb7623d960..2db41d82e35 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WasitoraCatDragonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WasitoraCatDragonToken.java
@@ -27,6 +27,9 @@
*/
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
@@ -38,8 +41,17 @@ import mage.abilities.keyword.FlyingAbility;
*/
public class WasitoraCatDragonToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+
+ static {
+ tokenImageSets.addAll(Arrays.asList("C17"));
+ }
+
public WasitoraCatDragonToken() {
super("Cat Dragon", "3/3 black, red, and green Cat Dragon creature token with flying");
+
+ availableImageSetCodes = tokenImageSets;
+ setOriginalExpansionSetCode("C17");
cardType.add(CardType.CREATURE);
this.subtype.add(SubType.CAT);
diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieKnightToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieKnightToken.java
index 136850dab42..ae28f573f43 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ZombieKnightToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ZombieKnightToken.java
@@ -1,5 +1,35 @@
+/*
+ * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of BetaSteward_at_googlemail.com.
+ */
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.keyword.MenaceAbility;
@@ -8,9 +38,16 @@ import mage.constants.SubType;
public class ZombieKnightToken extends TokenImpl {
+ final static private List tokenImageSets = new ArrayList<>();
+ static {
+ tokenImageSets.addAll(Arrays.asList("DOM"));
+ }
+
public ZombieKnightToken(){
super("Zombie Knight", "a 2/2 black Zombie Knight creature token with menace");
- color.addColor(ObjectColor.BLACK);
+ availableImageSetCodes = tokenImageSets;
+ setOriginalExpansionSetCode("DOM");
+ color.setBlack(true);
cardType.add(CardType.CREATURE);
subtype.add(SubType.ZOMBIE, SubType.KNIGHT);
addAbility(new MenaceAbility());
diff --git a/Mage/src/main/java/mage/game/permanent/token/custom/CreatureToken.java b/Mage/src/main/java/mage/game/permanent/token/custom/CreatureToken.java
new file mode 100644
index 00000000000..bad6c840a1b
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/custom/CreatureToken.java
@@ -0,0 +1,76 @@
+package mage.game.permanent.token.custom;
+
+import mage.MageInt;
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.TokenImpl;
+import mage.util.SubTypeList;
+
+/**
+ *
+ * @author JayDi85
+ */
+public class CreatureToken extends TokenImpl {
+
+ public CreatureToken() {
+ this(0, 0);
+ }
+
+ public CreatureToken(int power, int toughness) {
+ this(power, toughness, String.format("%d/%d creature", power, toughness));
+ }
+
+ public CreatureToken(int power, int toughness, String description) {
+ this(power, toughness, description, (SubTypeList) null);
+ }
+
+ public CreatureToken(int power, int toughness, String description, SubType extraSubType) {
+ this(power, toughness, description, new SubTypeList(extraSubType));
+ }
+
+ public CreatureToken(int power, int toughness, String description, SubTypeList extraSubTypes) {
+ super("", description);
+ this.cardType.add(CardType.CREATURE);
+ this.power = new MageInt(power);
+ this.toughness = new MageInt(toughness);
+
+ if (extraSubTypes != null) {
+ this.subtype.addAll(extraSubTypes);
+ }
+ }
+
+ public CreatureToken withAbility(Ability ability) {
+ this.addAbility(ability);
+ return this;
+ }
+
+ public CreatureToken withColor(String extraColors) {
+ ObjectColor extraColorsList = new ObjectColor(extraColors);
+ this.getColor(null).addColor(extraColorsList);
+ return this;
+ }
+
+ public CreatureToken withType(CardType extraType) {
+ if (!this.cardType.contains(extraType)) {
+ this.cardType.add(extraType);
+ }
+ return this;
+ }
+
+ public CreatureToken withSubType(SubType extraSubType) {
+ if (!this.subtype.contains(extraSubType)) {
+ this.subtype.add(extraSubType);
+ }
+ return this;
+ }
+
+ public CreatureToken(final CreatureToken token) {
+ super(token);
+ }
+
+ public CreatureToken copy() {
+ return new CreatureToken(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/custom/ElementalCreatureToken.java b/Mage/src/main/java/mage/game/permanent/token/custom/ElementalCreatureToken.java
new file mode 100644
index 00000000000..125bbbd8ee6
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/custom/ElementalCreatureToken.java
@@ -0,0 +1,48 @@
+package mage.game.permanent.token.custom;
+
+import mage.MageInt;
+import mage.ObjectColor;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.TokenImpl;
+import mage.util.SubTypeList;
+
+/**
+ *
+ * @author JayDi85
+ */
+public class ElementalCreatureToken extends TokenImpl {
+
+ public ElementalCreatureToken() {
+ this(0, 0);
+ }
+
+ public ElementalCreatureToken(int power, int toughness) {
+ this(power, toughness, String.format("%d/%d Elemental creature", power, toughness));
+ }
+
+ public ElementalCreatureToken(int power, int toughness, String description) {
+ this(power, toughness, description, (ObjectColor) null);
+ }
+
+ public ElementalCreatureToken(int power, int toughness, String description, ObjectColor color) {
+ super("", description);
+ this.cardType.add(CardType.CREATURE);
+ this.subtype.add(SubType.ELEMENTAL);
+
+ this.power = new MageInt(power);
+ this.toughness = new MageInt(toughness);
+
+ if (color != null) {
+ this.color.addColor(color);
+ }
+ }
+
+ public ElementalCreatureToken(final ElementalCreatureToken token) {
+ super(token);
+ }
+
+ public ElementalCreatureToken copy() {
+ return new ElementalCreatureToken(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index 693b324c910..adb17ec7ac9 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -149,6 +149,7 @@ public class Spell extends StackObjImpl implements Card {
this.resolving = spell.resolving;
this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities;
+ this.targetChanged = spell.targetChanged;
}
public boolean activate(Game game, boolean noMana) {
@@ -409,7 +410,7 @@ public class Spell extends StackObjImpl implements Card {
}
} else {
// Copied spell, only remove from stack
- game.getStack().remove(this);
+ game.getStack().remove(this, game);
}
}
@@ -771,7 +772,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) {
if (this.isCopiedSpell()) {
- game.getStack().remove(this);
+ game.getStack().remove(this, game);
return true;
}
return this.card.moveToExile(exileId, name, sourceId, game, appliedEffects);
diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java
index ed0bf563879..aa8246d0c5d 100644
--- a/Mage/src/main/java/mage/game/stack/SpellStack.java
+++ b/Mage/src/main/java/mage/game/stack/SpellStack.java
@@ -67,15 +67,16 @@ public class SpellStack extends ArrayDeque {
if (top != null) {
if (contains(top)) {
logger.warn("StackObject was still on the stack after resoving" + top.getName());
- this.remove(top);
+ this.remove(top, game);
}
}
}
}
- public boolean remove(StackObject object) {
+ public boolean remove(StackObject object, Game game) {
for (StackObject spell : this) {
if (spell.getId().equals(object.getId())) {
+ game.getState().setZone(spell.getId(), null);
return super.remove(spell);
}
}
@@ -107,7 +108,7 @@ public class SpellStack extends ArrayDeque {
}
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement
- this.remove(stackObject);
+ this.remove(stackObject, game);
}
stackObject.counter(sourceId, game, zone, owner, zoneDetail);
if (!game.isSimulation()) {
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index 00eabb629a5..74038eb306f 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -89,6 +89,7 @@ public class StackAbility extends StackObjImpl implements Ability {
this.name = stackAbility.name;
this.expansionSetCode = stackAbility.expansionSetCode;
this.targetAdjustment = stackAbility.targetAdjustment;
+ this.targetChanged = stackAbility.targetChanged;
}
@Override
@@ -100,14 +101,14 @@ public class StackAbility extends StackObjImpl implements Ability {
public boolean resolve(Game game) {
if (ability.getTargets().stillLegal(ability, game) || !canFizzle()) {
boolean result = ability.resolve(game);
- game.getStack().remove(this);
+ game.getStack().remove(this, game);
return result;
}
if (!game.isSimulation()) {
game.informPlayers("Ability has been fizzled: " + getRule());
}
counter(null, game);
- game.getStack().remove(this);
+ game.getStack().remove(this, game);
return false;
}
diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjImpl.java
index e58b35a8de2..12d49f57499 100644
--- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java
+++ b/Mage/src/main/java/mage/game/stack/StackObjImpl.java
@@ -5,6 +5,8 @@
*/
package mage.game.stack;
+import java.util.Set;
+import java.util.UUID;
import mage.MageObject;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
@@ -19,15 +21,14 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.TargetAmount;
-import java.util.Set;
-import java.util.UUID;
-
/**
*
* @author LevelX2
*/
public abstract class StackObjImpl implements StackObject {
+ protected boolean targetChanged; // for Psychic Battle
+
/**
* Choose new targets for a stack Object
*
@@ -72,12 +73,11 @@ public abstract class StackObjImpl implements StackObject {
* the change is legal.
*
* Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to
- * any target and 1 damage to another target creature or
- * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar
- * Elves, in that order. You cast Redirect, an instant that reads "You may
- * choose new targets for target spell," targeting Arc Trail. You can change
- * the first target to Llanowar Elves and change the second target to
- * Runeclaw Bear.
+ * any target and 1 damage to another target creature or player." The
+ * current targets of Arc Trail are Runeclaw Bear and Llanowar Elves, in
+ * that order. You cast Redirect, an instant that reads "You may choose new
+ * targets for target spell," targeting Arc Trail. You can change the first
+ * target to Llanowar Elves and change the second target to Runeclaw Bear.
*
* 114.7. Modal spells and abilities may have different targeting
* requirements for each mode. An effect that allows a player to change the
@@ -271,4 +271,14 @@ public abstract class StackObjImpl implements StackObject {
@Override
public void removePTCDA() {
}
+
+ @Override
+ public boolean isTargetChanged() {
+ return targetChanged;
+ }
+
+ @Override
+ public void setTargetChanged(boolean targetChanged) {
+ this.targetChanged = targetChanged;
+ }
}
diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java
index 7268f9ab03b..156db54c597 100644
--- a/Mage/src/main/java/mage/game/stack/StackObject.java
+++ b/Mage/src/main/java/mage/game/stack/StackObject.java
@@ -52,6 +52,10 @@ public interface StackObject extends MageObject, Controllable {
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets);
+
+ boolean isTargetChanged();
+
+ void setTargetChanged(boolean targetChanged);
@Override
StackObject copy();
diff --git a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
index 2ffa2a6acc3..c445b96bf54 100644
--- a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
+++ b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
@@ -98,6 +98,7 @@ public class TournamentOptions implements Serializable {
public void setPlaneChase(boolean planeChase) {
this.planeChase = planeChase;
+ this.matchOptions.setPlaneChase(planeChase);
}
public int getNumberRounds() {
diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java
index 3463487e455..221c771ea07 100644
--- a/Mage/src/main/java/mage/game/turn/Turn.java
+++ b/Mage/src/main/java/mage/game/turn/Turn.java
@@ -284,7 +284,7 @@ public class Turn implements Serializable {
if (stackObject instanceof Spell) {
((Spell) stackObject).moveToExile(null, "", source.getSourceId(), game);
} else {
- game.getStack().remove(stackObject); // stack ability
+ game.getStack().remove(stackObject, game); // stack ability
}
}
// 2) All attacking and blocking creatures are removed from combat.
diff --git a/Mage/src/main/java/mage/players/Library.java b/Mage/src/main/java/mage/players/Library.java
index 61c4734a68f..f928eea29a7 100644
--- a/Mage/src/main/java/mage/players/Library.java
+++ b/Mage/src/main/java/mage/players/Library.java
@@ -135,25 +135,18 @@ public class Library implements Serializable {
}
}
- public void putCardThirdFromTheTop(Card card, Game game) {
- if (card != null && card.getOwnerId().equals(playerId)) {
- Card cardTop = null;
- Card cardSecond = null;
- if (hasCards()) {
- cardTop = removeFromTop(game);
- }
- if (hasCards()) {
- cardSecond = removeFromTop(game);
+ public void putCardToTopXPos(Card card, int pos, Game game) {
+ if (card != null && pos > -1) {
+ LinkedList save = new LinkedList<>();
+ int idx = 1;
+ while (hasCards() && idx < pos) {
+ idx++;
+ save.add(removeFromTop(game));
}
putOnTop(card, game);
- if (cardSecond != null) {
- putOnTop(cardSecond, game);
+ while (!save.isEmpty()) {
+ putOnTop(save.removeLast(), game);
}
- if (cardTop != null) {
- putOnTop(cardTop, game);
- }
- } else {
- game.getPlayer(card.getOwnerId()).getLibrary().putCardThirdFromTheTop(card, game);
}
}
@@ -190,6 +183,12 @@ public class Library implements Serializable {
return new ArrayList<>(library);
}
+ /**
+ * Returns the cards of the library in a list ordered from top to buttom
+ *
+ * @param game
+ * @return
+ */
public List getCards(Game game) {
return library.stream().map(game::getCard).collect(Collectors.toList());
}
diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java
index 565e96cac21..2448b8a9220 100644
--- a/Mage/src/main/java/mage/players/ManaPool.java
+++ b/Mage/src/main/java/mage/players/ManaPool.java
@@ -395,7 +395,8 @@ public class ManaPool implements Serializable {
Mana mana = manaToAdd.copy();
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) {
if (mana instanceof ConditionalMana) {
- ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game), source.getOriginalId());
+ ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game),
+ ((ConditionalMana) mana).getManaProducerOriginalId() != null ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
@@ -505,4 +506,13 @@ public class ManaPool implements Serializable {
public boolean isEmpty() {
return count() == 0;
}
+
+ public List getManaItems() {
+ List itemsCopy = new ArrayList<>();
+ for (ManaPoolItem manaItem : manaItems) {
+ itemsCopy.add(manaItem.copy());
+ }
+ return itemsCopy;
+ }
+
}
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 83d698dafba..efb73fff230 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -474,14 +474,41 @@ public interface Player extends MageItem, Copyable {
void resetStoredBookmark(Game game);
+ void revealCards(Ability source, Cards cards, Game game);
+
void revealCards(String name, Cards cards, Game game);
+ void revealCards(Ability source, String name, Cards cards, Game game);
+
void revealCards(String name, Cards cards, Game game, boolean postToLog);
+ /**
+ * Adds the cards to the reveal window and adds the source object's id name
+ * to the title bar of the revealed cards window
+ *
+ * @param source
+ * @param name
+ * @param cards
+ * @param game
+ * @param postToLog
+ */
+ void revealCards(Ability source, String name, Cards cards, Game game, boolean postToLog);
+
void lookAtCards(String name, Card card, Game game);
void lookAtCards(String name, Cards cards, Game game);
+ /**
+ * Adds the cards to the look window and adds the source object's id name to
+ * the title bar of the lookedAt window
+ *
+ * @param source
+ * @param name
+ * @param cards
+ * @param game
+ */
+ void lookAtCards(Ability source, String name, Cards cards, Game game);
+
@Override
Player copy();
@@ -535,6 +562,17 @@ public interface Player extends MageItem, Copyable {
*/
boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
+ /**
+ * Moves the card to the top x position of the library
+ *
+ * @param card
+ * @param game
+ * @param source
+ * @param xFromTheTop
+ * @return
+ */
+ boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop);
+
/**
* Moves the cards from cards to the top of players library.
*
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index c850a67fcba..6a0524ddc61 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -84,6 +84,7 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
+import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.SquirrelToken;
import mage.game.stack.Spell;
import mage.game.stack.StackAbility;
@@ -894,6 +895,26 @@ public abstract class PlayerImpl implements Player, Serializable {
return true;
}
+ @Override
+ public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
+ if (card.getOwnerId().equals(getId())) {
+ if (library.size() + 1 < xFromTheTop) {
+ putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, true);
+ } else {
+ if (card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true) && !(card instanceof PermanentToken) && !card.isCopy()) {
+ card = getLibrary().removeFromTop(game);
+ getLibrary().putCardToTopXPos(card, xFromTheTop, game);
+ game.informPlayers(card.getLogName() + " is put into " + getLogName() + "'s library " + CardUtil.numberToOrdinalText(xFromTheTop) + " from the top");
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return game.getPlayer(card.getOwnerId()).putCardOnTopXOfLibrary(card, game, source, xFromTheTop);
+ }
+ return true;
+ }
+
/**
* Can be cards or permanents that go to library
*
@@ -1474,16 +1495,34 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
- public void revealCards(String name, Cards cards, Game game) {
- revealCards(name, cards, game, true);
+ public void revealCards(Ability source, Cards cards, Game game) {
+ revealCards(source, null, cards, game, true);
}
@Override
- public void revealCards(String name, Cards cards, Game game, boolean postToLog) {
+ public void revealCards(String titleSuffix, Cards cards, Game game) {
+ revealCards(titleSuffix, cards, game, true);
+ }
+
+ @Override
+ public void revealCards(String titleSuffix, Cards cards, Game game, boolean postToLog) {
+ revealCards(null, titleSuffix, cards, game, postToLog);
+ }
+
+ @Override
+ public void revealCards(Ability source, String titleSuffix, Cards cards, Game game) {
+ revealCards(source, titleSuffix, cards, game, true);
+ }
+
+ @Override
+ public void revealCards(Ability source, String titleSuffix, Cards cards, Game game, boolean postToLog) {
+ if (cards == null || cards.isEmpty()) {
+ return;
+ }
if (postToLog) {
- game.getState().getRevealed().add(name, cards);
+ game.getState().getRevealed().add(CardUtil.createObjectRealtedWindowTitle(source, game, titleSuffix), cards);
} else {
- game.getState().getRevealed().update(name, cards);
+ game.getState().getRevealed().update(CardUtil.createObjectRealtedWindowTitle(source, game, titleSuffix), cards);
}
if (postToLog && !game.isSimulation()) {
StringBuilder sb = new StringBuilder(getLogName()).append(" reveals ");
@@ -1500,14 +1539,19 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
- public void lookAtCards(String name, Card card, Game game) {
- game.getState().getLookedAt(this.playerId).add(name, card);
+ public void lookAtCards(String titleSuffix, Card card, Game game) {
+ game.getState().getLookedAt(this.playerId).add(titleSuffix, card);
game.fireUpdatePlayersEvent();
}
@Override
- public void lookAtCards(String name, Cards cards, Game game) {
- game.getState().getLookedAt(this.playerId).add(name, cards);
+ public void lookAtCards(String titleSuffix, Cards cards, Game game) {
+ this.lookAtCards(null, titleSuffix, cards, game);
+ }
+
+ @Override
+ public void lookAtCards(Ability source, String titleSuffix, Cards cards, Game game) {
+ game.getState().getLookedAt(this.playerId).add(CardUtil.createObjectRealtedWindowTitle(source, game, titleSuffix), cards);
game.fireUpdatePlayersEvent();
}
@@ -1817,6 +1861,7 @@ public abstract class PlayerImpl implements Player, Serializable {
return gainLife(amount, game, source.getSourceId());
}
+ @Override
public int gainLife(int amount, Game game, UUID sourceId) {
if (!canGainLife || amount == 0) {
return 0;
@@ -2226,7 +2271,7 @@ public abstract class PlayerImpl implements Player, Serializable {
Player opponent = game.getPlayer(opponentId);
if (opponent != null && !opponent.hasLost()) {
logger.debug("player won -> calling opponent lost: " + this.getName() + " opponent: " + opponent.getName());
- opponent.lost(game);
+ opponent.lostForced(game);
}
}
// if no more opponents alive, you win and the game ends
@@ -2499,7 +2544,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects) {
- return rollPlanarDie(game, appliedEffects, 1, 1);
+ return rollPlanarDie(game, appliedEffects, 2, 2);
}
/**
@@ -2514,15 +2559,15 @@ public abstract class PlayerImpl implements Player, Serializable {
*/
@Override
public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides) {
- int result = RandomUtil.nextInt(6) + 1;
+ int result = RandomUtil.nextInt(9) + 1;
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
- if (numberChaosSides + numberPlanarSides > 6) {
- numberChaosSides = 1;
- numberPlanarSides = 1;
+ if (numberChaosSides + numberPlanarSides > 9) {
+ numberChaosSides = 2;
+ numberPlanarSides = 2;
}
if (result <= numberChaosSides) {
roll = PlanarDieRoll.CHAOS_ROLL;
- } else if (result > 6 - numberPlanarSides) {
+ } else if (result > 9 - numberPlanarSides) {
roll = PlanarDieRoll.PLANAR_ROLL;
}
if (!game.isSimulation()) {
@@ -3687,7 +3732,7 @@ public abstract class PlayerImpl implements Player, Serializable {
final Spell spell = (Spell) card;
if (spell.isCopiedSpell()) {
// Copied spell, only remove from stack
- game.getStack().remove(spell);
+ game.getStack().remove(spell, game);
}
}
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
@@ -3866,7 +3911,7 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
- PlayerImpl obj = (PlayerImpl) o;
+ Player obj = (Player) o;
if (this.getId() == null || obj.getId() == null) {
return false;
}
diff --git a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java
index 0d16c674ad2..b40c02ce270 100644
--- a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java
+++ b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java
@@ -27,20 +27,20 @@
*/
package mage.target.common;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.cards.Cards;
import mage.constants.Zone;
import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.TargetCard;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-
/**
*
* @author BetaSteward_at_googlemail.com
@@ -48,7 +48,7 @@ import java.util.UUID;
public class TargetCardInYourGraveyard extends TargetCard {
public TargetCardInYourGraveyard() {
- this(1, 1, new FilterCard("card from your graveyard"));
+ this(1, 1, StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD);
}
public TargetCardInYourGraveyard(FilterCard filter) {
diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java
index e220491074e..f4ca2f05c36 100644
--- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java
+++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java
@@ -7,9 +7,11 @@ package mage.target.targetpointer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
+import mage.cards.Card;
import mage.cards.Cards;
import mage.game.Game;
import mage.game.permanent.Permanent;
@@ -46,6 +48,14 @@ public class FixedTargets implements TargetPointer {
this.initialized = true;
}
+ public FixedTargets(Set