forked from External/mage
This commit is contained in:
commit
9a52e15460
2419 changed files with 19807 additions and 23613 deletions
|
|
@ -588,7 +588,10 @@ public class ScryfallImageSupportCards {
|
|||
add("FIN"); // Final Fantasy
|
||||
add("FIC"); // Final Fantasy Commander
|
||||
add("FCA"); // Final Fantasy: Through the Ages
|
||||
add("EOE"); // Edge of Eternities
|
||||
add("EOC"); // Edge of Eternities Commander
|
||||
add("SPE"); // Marvel's Spider-Man Eternal
|
||||
add("TLA"); // Avatar: The Last Airbender
|
||||
|
||||
// Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks
|
||||
add("CALC"); // Custom Alchemized versions of existing cards
|
||||
|
|
|
|||
|
|
@ -352,6 +352,9 @@ public class ScryfallImageSupportTokens {
|
|||
put("ELD/Rat", "https://api.scryfall.com/cards/teld/6/en?format=image");
|
||||
put("ELD/Wolf", "https://api.scryfall.com/cards/teld/14/en?format=image");
|
||||
|
||||
// UND
|
||||
put("UND/Goblin", "https://api.scryfall.com/cards/tund/2?format=image");
|
||||
|
||||
// THB
|
||||
put("THB/Elemental", "https://api.scryfall.com/cards/tthb/8/en?format=image");
|
||||
put("THB/Goat", "https://api.scryfall.com/cards/tthb/1/en?format=image");
|
||||
|
|
@ -1843,6 +1846,12 @@ public class ScryfallImageSupportTokens {
|
|||
put("40K/Tyranid Gargoyle", "https://api.scryfall.com/cards/t40k/9/en?format=image");
|
||||
put("40K/Tyranid Warrior", "https://api.scryfall.com/cards/t40k/19/en?format=image");
|
||||
|
||||
// UNF
|
||||
put("UNF/Clown Robot/1", "https://api.scryfall.com/cards/tunf/2?format=image");
|
||||
put("UNF/Clown Robot/2", "https://api.scryfall.com/cards/tunf/3?format=image");
|
||||
put("UNF/Storm Crow", "https://api.scryfall.com/cards/tunf/5?format=image");
|
||||
put("UNF/Squirrel", "https://api.scryfall.com/cards/tunf/8?format=image");
|
||||
|
||||
// BRO
|
||||
put("BRO/Bear", "https://api.scryfall.com/cards/tbro/2/en?format=image");
|
||||
put("BRO/Construct/1", "https://api.scryfall.com/cards/tbro/5/en?format=image");
|
||||
|
|
@ -2026,6 +2035,7 @@ public class ScryfallImageSupportTokens {
|
|||
|
||||
// DIS
|
||||
put("DIS/Emblem Momir", "https://api.scryfall.com/cards/pmoa/61/en?format=image");
|
||||
put("DIS/Elemental", "https://api.scryfall.com/cards/togw/9?format=image");
|
||||
|
||||
// MUL
|
||||
put("MUL/Elemental", "https://api.scryfall.com/cards/tmul/2/en?format=image");
|
||||
|
|
@ -2182,6 +2192,7 @@ public class ScryfallImageSupportTokens {
|
|||
|
||||
// WHO
|
||||
put("WHO/Alien", "https://api.scryfall.com/cards/twho/2?format=image");
|
||||
put("WHO/Alien Angel", "https://api.scryfall.com/cards/twho/11?format=image");
|
||||
put("WHO/Alien Insect", "https://api.scryfall.com/cards/twho/19/en?format=image");
|
||||
put("WHO/Alien Rhino", "https://api.scryfall.com/cards/twho/3/en?format=image");
|
||||
put("WHO/Alien Salamander", "https://api.scryfall.com/cards/twho/16?format=image");
|
||||
|
|
@ -2536,6 +2547,7 @@ public class ScryfallImageSupportTokens {
|
|||
put("DSK/Everywhere", "https://api.scryfall.com/cards/tdsk/16?format=image");
|
||||
put("DSK/Glimmer", "https://api.scryfall.com/cards/tdsk/4?format=image");
|
||||
put("DSK/Gremlin", "https://api.scryfall.com/cards/tdsk/11?format=image");
|
||||
put("DSK/Horror", "https://api.scryfall.com/cards/tdsk/10?format=image");
|
||||
put("DSK/Insect/1", "https://api.scryfall.com/cards/tdsk/13?format=image");
|
||||
put("DSK/Insect/2", "https://api.scryfall.com/cards/tdsk/5?format=image");
|
||||
put("DSK/Primo, the Indivisible", "https://api.scryfall.com/cards/tdsk/14?format=image");
|
||||
|
|
@ -2698,7 +2710,8 @@ public class ScryfallImageSupportTokens {
|
|||
put("TDC/Gold", "https://api.scryfall.com/cards/ttdc/29/en?format=image");
|
||||
put("TDC/Human", "https://api.scryfall.com/cards/ttdc/5/en?format=image");
|
||||
put("TDC/Inkling", "https://api.scryfall.com/cards/ttdc/28?format=image");
|
||||
put("TDC/Insect", "https://api.scryfall.com/cards/ttdc/22/en?format=image");
|
||||
put("TDC/Insect/1", "https://api.scryfall.com/cards/ttdc/22/en?format=image");
|
||||
put("TDC/Insect/2", "https://api.scryfall.com/cards/ttdc/23/en?format=image");
|
||||
put("TDC/Karox Bladewing", "https://api.scryfall.com/cards/ttdc/19?format=image");
|
||||
put("TDC/Myr", "https://api.scryfall.com/cards/ttdc/30/en?format=image");
|
||||
put("TDC/Plant", "https://api.scryfall.com/cards/ttdc/24/en?format=image");
|
||||
|
|
@ -2710,6 +2723,7 @@ public class ScryfallImageSupportTokens {
|
|||
put("TDC/Spider", "https://api.scryfall.com/cards/ttdc/25?format=image");
|
||||
put("TDC/Spirit", "https://api.scryfall.com/cards/ttdc/6/en?format=image");
|
||||
put("TDC/Thopter", "https://api.scryfall.com/cards/ttdc/33/en?format=image");
|
||||
put("TDC/Wall", "https://api.scryfall.com/cards/ttdc/7/en?format=image");
|
||||
|
||||
// ACR
|
||||
put("ACR/Assassin", "https://api.scryfall.com/cards/tacr/4?format=image");
|
||||
|
|
@ -2723,7 +2737,51 @@ public class ScryfallImageSupportTokens {
|
|||
put("DD2/Elemental Shaman", "https://api.scryfall.com/cards/tdd2/1?format=image");
|
||||
|
||||
// FIN
|
||||
put("FIN/Hero/1", "https://api.scryfall.com/cards/tfin/2/en?format=image");
|
||||
put("FIN/Hero/2", "https://api.scryfall.com/cards/tfin/3/en?format=image");
|
||||
put("FIN/Hero/3", "https://api.scryfall.com/cards/tfin/4/en?format=image");
|
||||
put("FIN/Hero/4", "https://api.scryfall.com/cards/tfin/5/en?format=image");
|
||||
put("FIN/Hero/5", "https://api.scryfall.com/cards/tfin/6/en?format=image");
|
||||
put("FIN/Hero/6", "https://api.scryfall.com/cards/tfin/7/en?format=image");
|
||||
put("FIN/Hero/7", "https://api.scryfall.com/cards/tfin/8/en?format=image");
|
||||
put("FIN/Hero/8", "https://api.scryfall.com/cards/tfin/9/en?format=image");
|
||||
put("FIN/Hero/9", "https://api.scryfall.com/cards/tfin/26/en?format=image");
|
||||
put("FIN/Hero/10", "https://api.scryfall.com/cards/tfin/27/en?format=image");
|
||||
put("FIN/Hero/11", "https://api.scryfall.com/cards/tfin/28/en?format=image");
|
||||
put("FIN/Hero/12", "https://api.scryfall.com/cards/tfin/29/en?format=image");
|
||||
put("FIN/Hero/13", "https://api.scryfall.com/cards/tfin/30/en?format=image");
|
||||
put("FIN/Hero/14", "https://api.scryfall.com/cards/tfin/31/en?format=image");
|
||||
put("FIN/Hero/15", "https://api.scryfall.com/cards/tfin/32/en?format=image");
|
||||
put("FIN/Hero/16", "https://api.scryfall.com/cards/tfin/33/en?format=image");
|
||||
put("FIN/Knight", "https://api.scryfall.com/cards/tfin/10/en?format=image");
|
||||
put("FIN/Moogle/1", "https://api.scryfall.com/cards/tfin/11/en?format=image");
|
||||
put("FIN/Moogle/2", "https://api.scryfall.com/cards/tfin/34/en?format=image");
|
||||
put("FIN/Robot", "https://api.scryfall.com/cards/tfin/12/en?format=image");
|
||||
put("FIN/Horror", "https://api.scryfall.com/cards/tfin/13/en?format=image");
|
||||
put("FIN/Wizard/1", "https://api.scryfall.com/cards/tfin/14/en?format=image");
|
||||
put("FIN/Wizard/2", "https://api.scryfall.com/cards/tfin/15/en?format=image");
|
||||
put("FIN/Wizard/3", "https://api.scryfall.com/cards/tfin/35/en?format=image");
|
||||
put("FIN/Bird/1", "https://api.scryfall.com/cards/tfin/16/en?format=image");
|
||||
put("FIN/Bird/2", "https://api.scryfall.com/cards/tfin/17/en?format=image");
|
||||
put("FIN/Frog", "https://api.scryfall.com/cards/tfin/18/en?format=image");
|
||||
put("FIN/Angelo", "https://api.scryfall.com/cards/tfin/19/en?format=image");
|
||||
put("FIN/Darkstar", "https://api.scryfall.com/cards/tfin/20/en?format=image");
|
||||
put("FIN/Elemental", "https://api.scryfall.com/cards/tfin/21/en?format=image");
|
||||
put("FIN/Food", "https://api.scryfall.com/cards/tfin/22?format=image");
|
||||
put("FIN/Treasure/1", "https://api.scryfall.com/cards/tfin/23/en?format=image");
|
||||
put("FIN/Treasure/2", "https://api.scryfall.com/cards/tfin/36/en?format=image");
|
||||
put("FIN/Emblem Sephiroth", "https://api.scryfall.com/cards/tfin/24/en?format=image");
|
||||
|
||||
// FIC
|
||||
put("FIC/Human Soldier", "https://api.scryfall.com/cards/tfic/1/en?format=image");
|
||||
put("FIC/Soldier", "https://api.scryfall.com/cards/tfic/2/en?format=image");
|
||||
put("FIC/Spirit", "https://api.scryfall.com/cards/tfic/3/en?format=image");
|
||||
put("FIC/Bird", "https://api.scryfall.com/cards/tfic/4/en?format=image");
|
||||
put("FIC/Squid", "https://api.scryfall.com/cards/tfic/5/en?format=image");
|
||||
put("FIC/Zombie", "https://api.scryfall.com/cards/tfic/6/en?format=image");
|
||||
put("FIC/Rebel", "https://api.scryfall.com/cards/tfic/7/en?format=image");
|
||||
put("FIC/The Blackjack", "https://api.scryfall.com/cards/tfic/8/en?format=image");
|
||||
put("FIC/Clue", "https://api.scryfall.com/cards/tfic/9/en?format=image");
|
||||
|
||||
// JVC
|
||||
put("JVC/Elemental Shaman", "https://api.scryfall.com/cards/tjvc/4?format=image");
|
||||
|
|
@ -2756,6 +2814,7 @@ public class ScryfallImageSupportTokens {
|
|||
// UGL
|
||||
put("UGL/Goblin", "https://api.scryfall.com/cards/tugl/4?format=image");
|
||||
put("UGL/Pegasus", "https://api.scryfall.com/cards/tugl/1?format=image");
|
||||
put("UGL/Rabid Sheep", "https://api.scryfall.com/cards/tugl/5?format=image");
|
||||
put("UGL/Soldier", "https://api.scryfall.com/cards/tugl/2?format=image");
|
||||
put("UGL/Squirrel", "https://api.scryfall.com/cards/tugl/6?format=image");
|
||||
put("UGL/Zombie", "https://api.scryfall.com/cards/tugl/3?format=image");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class AmountTestableResult extends BaseTestableResult {
|
||||
|
||||
int amount = 0;
|
||||
|
||||
public void onFinish(String resDebugSource, boolean status, List<String> info, int amount) {
|
||||
this.onFinish(resDebugSource, status, info);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResAssert() {
|
||||
return null; // TODO: implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear() {
|
||||
super.onClear();
|
||||
this.amount = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package mage.utils.testers;
|
|||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -25,7 +26,8 @@ class AnnounceXTestableDialog extends BaseTestableDialog {
|
|||
|
||||
public AnnounceXTestableDialog(boolean isYou, boolean isMana, int min, int max) {
|
||||
super(String.format("player.announceX(%s)", isYou ? "you" : "AI"),
|
||||
String.format("%s from %d to %d", isMana ? "mana" : "cost", min, max), "");
|
||||
String.format("%s from %d to %d", isMana ? "mana" : "cost", min, max), "",
|
||||
new AmountTestableResult());
|
||||
this.isYou = isYou;
|
||||
this.isMana = isMana;
|
||||
this.min = min;
|
||||
|
|
@ -33,14 +35,15 @@ class AnnounceXTestableDialog extends BaseTestableDialog {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
String message = "<font color=green>message</font> with html";
|
||||
int chooseRes;
|
||||
chooseRes = choosingPlayer.announceX(this.min, this.max, message, game, source, this.isMana);
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||
return result;
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
int chooseRes = choosingPlayer.announceX(this.min, this.max, message, game, source, this.isMana);
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||
|
||||
((AmountTestableResult) this.getResult()).onFinish(chooseDebugSource, true, res, chooseRes);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetPermanentOrPlayer;
|
||||
|
||||
/**
|
||||
|
|
@ -15,14 +16,27 @@ import mage.target.common.TargetPermanentOrPlayer;
|
|||
*/
|
||||
abstract class BaseTestableDialog implements TestableDialog {
|
||||
|
||||
private Integer regNumber; // dialog number in runner (use it to find results and debugging)
|
||||
private final String group;
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final TestableResult result;
|
||||
|
||||
public BaseTestableDialog(String group, String name, String description) {
|
||||
public BaseTestableDialog(String group, String name, String description, TestableResult result) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegNumber(Integer regNumber) {
|
||||
this.regNumber = regNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRegNumber() {
|
||||
return this.regNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -41,13 +55,23 @@ abstract class BaseTestableDialog implements TestableDialog {
|
|||
}
|
||||
|
||||
@Override
|
||||
final public void showResult(Player player, Game game, String result) {
|
||||
public void prepare() {
|
||||
this.result.onClear();
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void showResult(Player player, Game game) {
|
||||
// show message with result
|
||||
game.informPlayer(player, result);
|
||||
game.informPlayer(player, String.join("<br>", getResult().getResDetails()));
|
||||
// reset game and gui (in most use cases it must return to player's priority)
|
||||
game.firePriorityEvent(player.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestableResult getResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
static Target createAnyTarget(int min, int max) {
|
||||
return createAnyTarget(min, max, false);
|
||||
}
|
||||
|
|
@ -56,19 +80,18 @@ abstract class BaseTestableDialog implements TestableDialog {
|
|||
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
||||
}
|
||||
|
||||
static Target createCreatureTarget(int min, int max) {
|
||||
return createCreatureTarget(min, max, false);
|
||||
}
|
||||
private static final FilterPermanent impossibleFilter = new FilterPermanent();
|
||||
|
||||
private static Target createCreatureTarget(int min, int max, boolean notTarget) {
|
||||
return new TargetCreaturePermanent(min, max).withNotTarget(notTarget);
|
||||
static {
|
||||
impossibleFilter.add(new ManaValuePredicate(ComparisonType.OR_LESS, -1));
|
||||
}
|
||||
|
||||
static Target createImpossibleTarget(int min, int max) {
|
||||
return createImpossibleTarget(min, max, false);
|
||||
return new TargetPermanent(min, max, impossibleFilter);
|
||||
}
|
||||
|
||||
private static Target createImpossibleTarget(int min, int max, boolean notTarget) {
|
||||
return new TargetCreaturePermanent(min, max, new FilterCreaturePermanent(SubType.TROOPER, "rare type"), notTarget);
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getGroup() + " - " + this.getName() + " - " + this.getDescription();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class BaseTestableResult implements TestableResult {
|
||||
|
||||
boolean isFinished = false;
|
||||
String resDebugSource = ""; // source code line to find starting place to debug
|
||||
boolean resStatus = false;
|
||||
List<String> resInfo = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String getResDebugSource() {
|
||||
return this.resDebugSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResStatus() {
|
||||
return this.resStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getResDetails() {
|
||||
return this.resInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResAssert() {
|
||||
return null; // TODO: implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(String resDebugSource, boolean resStatus, List<String> resDetails) {
|
||||
this.isFinished = true;
|
||||
this.resDebugSource = resDebugSource;
|
||||
this.resStatus = resStatus;
|
||||
this.resInfo = resDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return this.isFinished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear() {
|
||||
this.isFinished = false;
|
||||
this.resStatus = false;
|
||||
this.resInfo.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ChoiceTestableResult extends BaseTestableResult {
|
||||
|
||||
String choice = null;
|
||||
|
||||
public void onFinish(String resDebugSource, boolean status, List<String> info, String choice) {
|
||||
this.onFinish(resDebugSource, status, info);
|
||||
this.choice = choice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResAssert() {
|
||||
return null; // TODO: implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear() {
|
||||
super.onClear();
|
||||
this.choice = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import mage.players.Player;
|
|||
import mage.target.TargetAmount;
|
||||
import mage.target.Targets;
|
||||
import mage.target.common.TargetAnyTargetAmount;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -32,27 +33,39 @@ class ChooseAmountTestableDialog extends BaseTestableDialog {
|
|||
public ChooseAmountTestableDialog(boolean isYou, String name, int distributeAmount, int targetsMin, int targetsMax) {
|
||||
super(String.format("player.chooseTarget(%s, amount)", isYou ? "you" : "AI"),
|
||||
name,
|
||||
String.format("%d between %d-%d targets", distributeAmount, targetsMin, targetsMax));
|
||||
String.format("%d between %d-%d targets", distributeAmount, targetsMin, targetsMax),
|
||||
new TargetTestableResult());
|
||||
this.isYou = isYou;
|
||||
this.distributeAmount = distributeAmount;
|
||||
this.targetsMin = targetsMin;
|
||||
this.targetsMax = targetsMax;
|
||||
}
|
||||
|
||||
private ChooseAmountTestableDialog aiMustChoose(boolean resStatus, int targetsCount) {
|
||||
// TODO: AI use default distribution, improve someday
|
||||
TargetTestableResult res = ((TargetTestableResult) this.getResult());
|
||||
res.aiAssertEnabled = true;
|
||||
res.aiAssertResStatus = resStatus;
|
||||
res.aiAssertTargetsCount = targetsCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
TargetAmount choosingTarget = new TargetAnyTargetAmount(this.distributeAmount, this.targetsMin, this.targetsMax);
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
|
||||
// TODO: add "damage" word in ability text, so chooseTargetAmount an show diff dialog (due inner logic - distribute damage or 1/1)
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
boolean chooseRes = choosingPlayer.chooseTargetAmount(Outcome.Benefit, choosingTarget, source, game);
|
||||
List<String> result = new ArrayList<>();
|
||||
List<String> res = new ArrayList<>();
|
||||
if (chooseRes) {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, res);
|
||||
} else {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, res);
|
||||
}
|
||||
return result;
|
||||
|
||||
((TargetTestableResult) this.getResult()).onFinish(chooseDebugSource, chooseRes, res, choosingTarget);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
@ -61,53 +74,55 @@ class ChooseAmountTestableDialog extends BaseTestableDialog {
|
|||
|
||||
List<Boolean> isYous = Arrays.asList(false, true);
|
||||
|
||||
// current AI will choose 1 target and assign all values to it (except with outcome.Damage)
|
||||
// TODO: add use cases for damage effects?
|
||||
for (boolean isYou : isYous) {
|
||||
// up to
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 0).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 1).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 3).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 5).aiMustChoose(false, 0));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 1, 0, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 1, 0, 0).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 2, 0, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 2, 0, 0).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 3, 0, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 3, 0, 0).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 5, 0, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 5, 0, 0).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 5).aiMustChoose(true, 1));
|
||||
|
||||
// need target
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 1).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 3).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 5).aiMustChoose(false, 0));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 5).aiMustChoose(true, 1));
|
||||
//
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 3));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 5));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 1).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 3).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 5).aiMustChoose(true, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.Targets;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -36,16 +37,17 @@ class ChooseCardsTestableDialog extends BaseTestableDialog {
|
|||
|
||||
public ChooseCardsTestableDialog(boolean isTargetChoice, boolean notTarget, boolean isYou, String name, TargetCard target) {
|
||||
super(String.format("%s(%s, %s, cards)",
|
||||
isTargetChoice ? "player.chooseTarget" : "player.choose",
|
||||
isYou ? "you" : "AI",
|
||||
notTarget ? "not target" : "target"), name, target.toString());
|
||||
isTargetChoice ? "player.chooseTarget" : "player.choose",
|
||||
isYou ? "you" : "AI",
|
||||
notTarget ? "not target" : "target"), name, target.toString(),
|
||||
new TargetTestableResult());
|
||||
this.isTargetChoice = isTargetChoice;
|
||||
this.target = target.withNotTarget(notTarget);
|
||||
this.isYou = isYou;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
TargetCard choosingTarget = this.target.copy();
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
|
||||
|
|
@ -56,19 +58,23 @@ class ChooseCardsTestableDialog extends BaseTestableDialog {
|
|||
Cards choosingCards = new CardsImpl(all.stream().limit(100).collect(Collectors.toList()));
|
||||
|
||||
boolean chooseRes;
|
||||
String chooseDebugSource;
|
||||
if (this.isTargetChoice) {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingPlayer.chooseTarget(Outcome.Benefit, choosingCards, choosingTarget, source, game);
|
||||
} else {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingPlayer.choose(Outcome.Benefit, choosingCards, choosingTarget, source, game);
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
List<String> res = new ArrayList<>();
|
||||
if (chooseRes) {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, res);
|
||||
} else {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, res);
|
||||
}
|
||||
return result;
|
||||
|
||||
((TargetTestableResult) this.getResult()).onFinish(chooseDebugSource, chooseRes, res, choosingTarget);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mage.choices.*;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -24,28 +25,36 @@ class ChooseChoiceTestableDialog extends BaseTestableDialog {
|
|||
Choice choice;
|
||||
|
||||
public ChooseChoiceTestableDialog(boolean isYou, String name, Choice choice) {
|
||||
super(String.format("player.choose(%s, choice)", isYou ? "you" : "AI"), name, choice.getClass().getSimpleName());
|
||||
super(String.format("player.choose(%s, choice)", isYou ? "you" : "AI"),
|
||||
name,
|
||||
choice.getClass().getSimpleName(),
|
||||
new ChoiceTestableResult()
|
||||
);
|
||||
this.isYou = isYou;
|
||||
this.choice = choice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
Choice dialog = this.choice.copy();
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
boolean chooseRes = choosingPlayer.choose(Outcome.Benefit, dialog, game);
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||
result.add("");
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||
res.add("");
|
||||
String choice;
|
||||
if (dialog.isKeyChoice()) {
|
||||
String key = dialog.getChoiceKey();
|
||||
result.add(String.format("* selected key: %s (%s)", key, dialog.getKeyChoices().getOrDefault(key, null)));
|
||||
choice = dialog.getKeyChoices().getOrDefault(key, null);
|
||||
res.add(String.format("* selected key: %s (%s)", key, choice));
|
||||
} else {
|
||||
result.add(String.format("* selected value: %s", dialog.getChoice()));
|
||||
choice = dialog.getChoice();
|
||||
res.add(String.format("* selected value: %s", choice));
|
||||
}
|
||||
|
||||
return result;
|
||||
((ChoiceTestableResult) this.getResult()).onFinish(chooseDebugSource, chooseRes, res, choice);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -28,14 +29,18 @@ class ChoosePileTestableDialog extends BaseTestableDialog {
|
|||
int pileSize2;
|
||||
|
||||
public ChoosePileTestableDialog(boolean isYou, int pileSize1, int pileSize2) {
|
||||
super(String.format("player.choosePile(%s)", isYou ? "you" : "AI"), "pile sizes: " + pileSize1 + " and " + pileSize2, "");
|
||||
super(String.format("player.choosePile(%s)", isYou ? "you" : "AI"),
|
||||
"pile sizes: " + pileSize1 + " and " + pileSize2,
|
||||
"",
|
||||
new BaseTestableResult()
|
||||
);
|
||||
this.isYou = isYou;
|
||||
this.pileSize1 = pileSize1;
|
||||
this.pileSize2 = pileSize2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
// TODO: it's ok to show broken title - must add html support in windows's title someday
|
||||
String mainMessage = "main <font color=green>message</font> with html" + CardUtil.getSourceLogName(game, source);
|
||||
|
||||
|
|
@ -47,11 +52,13 @@ class ChoosePileTestableDialog extends BaseTestableDialog {
|
|||
List<Card> pile2 = all.stream().limit(this.pileSize2).collect(Collectors.toList());
|
||||
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
boolean chooseRes = choosingPlayer.choosePile(Outcome.Benefit, mainMessage, pile1, pile2, game);
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||
result.add(" * selected pile: " + (chooseRes ? "pile 1" : "pile 2"));
|
||||
return result;
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||
res.add(" * selected pile: " + (chooseRes ? "pile 1" : "pile 2"));
|
||||
|
||||
this.getResult().onFinish(chooseDebugSource, chooseRes, res);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.Targets;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -31,10 +32,14 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
|||
|
||||
public ChooseTargetTestableDialog(boolean isPlayerChoice, boolean isTargetChoice, boolean notTarget, boolean isYou, String name, Target target) {
|
||||
super(String.format("%s%s(%s, %s)",
|
||||
isPlayerChoice ? "player.choose" : "target.choose",
|
||||
isTargetChoice ? "Target" : "", // chooseTarget or choose
|
||||
isYou ? "you" : "AI",
|
||||
notTarget ? "not target" : "target"), name, target.toString());
|
||||
isPlayerChoice ? "player.choose" : "target.choose",
|
||||
isTargetChoice ? "Target" : "", // chooseTarget or choose
|
||||
isYou ? "you" : "AI",
|
||||
notTarget ? "not target" : "target"),
|
||||
name,
|
||||
target.toString(),
|
||||
new TargetTestableResult()
|
||||
);
|
||||
this.isPlayerChoice = isPlayerChoice;
|
||||
this.isTargetChoice = isTargetChoice;
|
||||
this.target = target.withNotTarget(notTarget);
|
||||
|
|
@ -42,34 +47,48 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Target choosingTarget = this.target.copy();
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
|
||||
boolean chooseRes;
|
||||
String chooseDebugSource;
|
||||
if (this.isPlayerChoice) {
|
||||
// player.chooseXXX
|
||||
if (this.isTargetChoice) {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingPlayer.chooseTarget(Outcome.Benefit, choosingTarget, source, game);
|
||||
} else {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingPlayer.choose(Outcome.Benefit, choosingTarget, source, game);
|
||||
}
|
||||
} else {
|
||||
// target.chooseXXX
|
||||
if (this.isTargetChoice) {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingTarget.chooseTarget(Outcome.Benefit, choosingPlayer.getId(), source, game);
|
||||
} else {
|
||||
chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
chooseRes = choosingTarget.choose(Outcome.Benefit, choosingPlayer.getId(), source, game);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
List<String> res = new ArrayList<>();
|
||||
if (chooseRes) {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, res);
|
||||
} else {
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, res);
|
||||
}
|
||||
return result;
|
||||
|
||||
((TargetTestableResult) this.getResult()).onFinish(chooseDebugSource, chooseRes, res, choosingTarget);
|
||||
}
|
||||
|
||||
private ChooseTargetTestableDialog aiMustChoose(boolean resStatus, int targetsCount) {
|
||||
TargetTestableResult res = ((TargetTestableResult) this.getResult());
|
||||
res.aiAssertEnabled = true;
|
||||
res.aiAssertResStatus = resStatus;
|
||||
res.aiAssertTargetsCount = targetsCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
@ -84,37 +103,29 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
|||
for (boolean isYou : isYous) {
|
||||
for (boolean isTargetChoice : isTargetChoices) {
|
||||
for (boolean isPlayerChoice : isPlayerChoices) {
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0 e.g. X=0", createAnyTarget(0, 0))); // simulate X=0
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1", createAnyTarget(1, 1)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3", createAnyTarget(3, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 5", createAnyTarget(5, 5)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any max", createAnyTarget(0, Integer.MAX_VALUE)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-1", createAnyTarget(0, 1)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-3", createAnyTarget(0, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-5", createAnyTarget(0, 5)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-3", createAnyTarget(1, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-3", createAnyTarget(2, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-5", createAnyTarget(1, 5)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-5", createAnyTarget(2, 5)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3-5", createAnyTarget(3, 5)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 4-5", createAnyTarget(4, 5))); // impossible on 3 targets
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0 e.g. X=0", createAnyTarget(0, 0)).aiMustChoose(false, 0)); // simulate X=0
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1", createAnyTarget(1, 1)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3", createAnyTarget(3, 3)).aiMustChoose(true, 3));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 5", createAnyTarget(5, 5)).aiMustChoose(true, 5));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any max", createAnyTarget(0, Integer.MAX_VALUE)).aiMustChoose(true, 6 + 1)); // 6 own cards + 1 own player
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-1", createAnyTarget(0, 1)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-3", createAnyTarget(0, 3)).aiMustChoose(true, 3));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-5", createAnyTarget(0, 5)).aiMustChoose(true, 5));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-3", createAnyTarget(1, 3)).aiMustChoose(true, 3));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-3", createAnyTarget(2, 3)).aiMustChoose(true, 3));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-5", createAnyTarget(1, 5)).aiMustChoose(true, 5));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-5", createAnyTarget(2, 5)).aiMustChoose(true, 5));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3-5", createAnyTarget(3, 5)).aiMustChoose(true, 5));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 4-5", createAnyTarget(4, 5)).aiMustChoose(true, 5));
|
||||
//
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0, e.g. X=0", createImpossibleTarget(0, 0)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1", createImpossibleTarget(1, 1)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 3", createImpossibleTarget(3, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-1", createImpossibleTarget(0, 1)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-3", createImpossibleTarget(0, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)));
|
||||
//
|
||||
/*
|
||||
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 0, e.g. X=0", createCreatureTarget(0, 0))); // simulate X=0
|
||||
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 1", createCreatureTarget(1, 1)));
|
||||
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 3", createCreatureTarget(3, 3)));
|
||||
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 5", createCreatureTarget(5, 5)));
|
||||
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures max", createCreatureTarget(0, Integer.MAX_VALUE)));
|
||||
*/
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0, e.g. X=0", createImpossibleTarget(0, 0)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1", createImpossibleTarget(1, 1)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 3", createImpossibleTarget(3, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-1", createImpossibleTarget(0, 1)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-3", createImpossibleTarget(0, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -27,7 +28,11 @@ class ChooseUseTestableDialog extends BaseTestableDialog {
|
|||
String messageAdditional;
|
||||
|
||||
public ChooseUseTestableDialog(boolean isYou, String name, String trueText, String falseText, String messageMain, String messageAdditional) {
|
||||
super(String.format("player.chooseUse(%s)", isYou ? "you" : "AI"), name + buildName(trueText, falseText, messageMain, messageAdditional), "");
|
||||
super(String.format("player.chooseUse(%s)", isYou ? "you" : "AI"),
|
||||
name + buildName(trueText, falseText, messageMain, messageAdditional),
|
||||
"",
|
||||
new BaseTestableResult()
|
||||
);
|
||||
this.isYou = isYou;
|
||||
this.trueText = trueText;
|
||||
this.falseText = falseText;
|
||||
|
|
@ -42,8 +47,9 @@ class ChooseUseTestableDialog extends BaseTestableDialog {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
boolean chooseRes = choosingPlayer.chooseUse(
|
||||
Outcome.Benefit,
|
||||
messageMain,
|
||||
|
|
@ -53,9 +59,10 @@ class ChooseUseTestableDialog extends BaseTestableDialog {
|
|||
source,
|
||||
game
|
||||
);
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(chooseRes ? "TRUE" : "FALSE");
|
||||
return result;
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(chooseRes ? "TRUE" : "FALSE");
|
||||
|
||||
this.getResult().onFinish(chooseDebugSource, chooseRes, res);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.utils.testers;
|
|||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -26,21 +27,25 @@ class GetAmountTestableDialog extends BaseTestableDialog {
|
|||
|
||||
public GetAmountTestableDialog(boolean isYou, int min, int max) {
|
||||
super(String.format("player.getAmount(%s)", isYou ? "you" : "AI"),
|
||||
String.format("from %d to %d", min, max), "");
|
||||
String.format("from %d to %d", min, max),
|
||||
"",
|
||||
new AmountTestableResult()
|
||||
);
|
||||
this.isYou = isYou;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
String message = "<font color=green>message</font> with html";
|
||||
int chooseRes;
|
||||
chooseRes = choosingPlayer.getAmount(this.min, this.max, message, source, game);
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||
return result;
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
int chooseRes = choosingPlayer.getAmount(this.min, this.max, message, source, game);
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||
|
||||
((AmountTestableResult) this.getResult()).onFinish(chooseDebugSource, true, res, chooseRes);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ import mage.constants.MultiAmountType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.DebugUtil;
|
||||
import mage.util.MultiAmountMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
|
|
@ -36,7 +38,9 @@ class GetMultiAmountTestableDialog extends BaseTestableDialog {
|
|||
public GetMultiAmountTestableDialog(boolean isYou, String info, int totalMin, int totalMax, List<List<Integer>> options) {
|
||||
super(String.format("player.getMultiAmount(%s)", isYou ? "you" : "AI"),
|
||||
String.format("%s, %d options from [%d-%d]", info, options.size(), totalMin, totalMax),
|
||||
"");
|
||||
"",
|
||||
new MultiAmountTestableResult()
|
||||
);
|
||||
this.isYou = isYou;
|
||||
this.totalMin = totalMin;
|
||||
this.totalMax = totalMax;
|
||||
|
|
@ -48,13 +52,33 @@ class GetMultiAmountTestableDialog extends BaseTestableDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private GetMultiAmountTestableDialog aiMustChoose(Integer... needValues) {
|
||||
// TODO: AI use default distribution (min possible values), improve someday
|
||||
MultiAmountTestableResult res = ((MultiAmountTestableResult) this.getResult());
|
||||
res.aiAssertEnabled = true;
|
||||
res.aiAssertValues = Arrays.stream(needValues).collect(Collectors.toList());
|
||||
return this;
|
||||
}
|
||||
|
||||
private GetMultiAmountTestableDialog aiMustChooseMany(Integer options, Integer perOption) {
|
||||
List<Integer> need = new ArrayList<>();
|
||||
IntStream.rangeClosed(1, options).forEach(x -> {
|
||||
need.add(perOption);
|
||||
});
|
||||
|
||||
MultiAmountTestableResult res = ((MultiAmountTestableResult) this.getResult());
|
||||
res.aiAssertEnabled = true;
|
||||
res.aiAssertValues = need;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
public void showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
Player choosingPlayer = this.isYou ? player : opponent;
|
||||
//String message = "<font color=green>message</font> with html";
|
||||
List<Integer> chooseRes;
|
||||
List<MultiAmountMessage> options = this.amountOptions.stream().map(MultiAmountMessage::copy).collect(Collectors.toList());
|
||||
chooseRes = choosingPlayer.getMultiAmountWithIndividualConstraints(
|
||||
String chooseDebugSource = DebugUtil.getMethodNameWithSource(0, "class");
|
||||
List<Integer> chooseRes = choosingPlayer.getMultiAmountWithIndividualConstraints(
|
||||
Outcome.Benefit,
|
||||
options,
|
||||
this.totalMin,
|
||||
|
|
@ -63,24 +87,24 @@ class GetMultiAmountTestableDialog extends BaseTestableDialog {
|
|||
game
|
||||
);
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(getGroup() + " - " + this.getName());
|
||||
List<String> res = new ArrayList<>();
|
||||
res.add(getGroup() + " - " + this.getName());
|
||||
int selectedIndex = -1;
|
||||
int selectedTotal = 0;
|
||||
for (Integer selectedValue : chooseRes) {
|
||||
selectedIndex++;
|
||||
selectedTotal += selectedValue;
|
||||
MultiAmountMessage option = this.amountOptions.get(selectedIndex);
|
||||
result.add(String.format("%d from [%d-%d, def %d]",
|
||||
res.add(String.format("%d from [%d-%d, def %d]",
|
||||
selectedValue,
|
||||
option.min,
|
||||
option.max,
|
||||
option.defaultValue
|
||||
));
|
||||
}
|
||||
result.add("total selected: " + selectedTotal);
|
||||
res.add("total selected: " + selectedTotal);
|
||||
|
||||
return result;
|
||||
((MultiAmountTestableResult) this.getResult()).onFinish(chooseDebugSource, true, res, chooseRes);
|
||||
}
|
||||
|
||||
static public void register(TestableDialogsRunner runner) {
|
||||
|
|
@ -88,28 +112,29 @@ class GetMultiAmountTestableDialog extends BaseTestableDialog {
|
|||
for (boolean isYou : isYous) {
|
||||
// make sure default values are valid due min/max settings
|
||||
|
||||
// TODO: add bad effect for AI (must test default distribution)
|
||||
|
||||
// single target
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 1, genSameOptions(1, 0, 1, 0)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 3, genSameOptions(1, 0, 3, 0)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 1, genSameOptions(1, 1, 1, 1)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 3, genSameOptions(1, 1, 3, 1)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 5 def", 0, 10, genSameOptions(1, 0, 10, 5)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 10 def", 10, 10, genSameOptions(1, 0, 10, 10)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 1, genSameOptions(1, 0, 1, 0)).aiMustChoose(1));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 3, genSameOptions(1, 0, 3, 0)).aiMustChoose(3));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 1, genSameOptions(1, 1, 1, 1)).aiMustChoose(1));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 3, genSameOptions(1, 1, 3, 1)).aiMustChoose(3));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 5 def", 0, 10, genSameOptions(1, 0, 10, 5)).aiMustChoose(10));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 10 def", 10, 10, genSameOptions(1, 0, 10, 10)).aiMustChoose(10));
|
||||
// multiple targets
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 0 def", 0, 5, genSameOptions(3, 0, 3, 0)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 0 def", 0, 5, genSameOptions(3, 0, 3, 0)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 1 def", 1, 5, genSameOptions(3, 1, 3, 1)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 1 def", 1, 5, genSameOptions(3, 1, 3, 1)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 0, 60, genSameOptions(3, 0, 60, 20)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 60, 60, genSameOptions(3, 0, 60, 20)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 0 def", 0, 5, genSameOptions(3, 0, 3, 0)).aiMustChoose(2, 2, 1));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 1 def", 1, 5, genSameOptions(3, 1, 3, 1)).aiMustChoose(2, 2, 1));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 0, 60, genSameOptions(3, 0, 60, 20)).aiMustChoose(20, 20, 20));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 60, 60, genSameOptions(3, 0, 60, 20)).aiMustChoose(20, 20, 20));
|
||||
// big lists
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "big list", 0, 100, genSameOptions(20, 0, 100, 0)));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "big list", 0, 100, genSameOptions(20, 0, 100, 0)).aiMustChooseMany(20, 5));
|
||||
runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "big list", 0, 100, genSameOptions(100, 0, 100, 0)).aiMustChooseMany(100, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<List<Integer>> genSameOptions(int amount, int min, int max, int def) {
|
||||
private static List<List<Integer>> genSameOptions(int options, int min, int max, int def) {
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
for (int i = 0; i < amount; i++) {
|
||||
for (int i = 0; i < options; i++) {
|
||||
// min, max, default
|
||||
res.add(Arrays.asList(min, max, def));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class MultiAmountTestableResult extends BaseTestableResult {
|
||||
|
||||
List<Integer> selectedValues;
|
||||
|
||||
boolean aiAssertEnabled = false;
|
||||
List<Integer> aiAssertValues = new ArrayList<>();
|
||||
|
||||
public void onFinish(String resDebugSource, boolean status, List<String> info, List<Integer> selectedValues) {
|
||||
this.onFinish(resDebugSource, status, info);
|
||||
this.selectedValues = selectedValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResAssert() {
|
||||
if (!this.aiAssertEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// not finished
|
||||
if (this.selectedValues == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// wrong selection
|
||||
String selected = this.selectedValues.toString();
|
||||
String need = this.aiAssertValues.toString();
|
||||
|
||||
if (!selected.equals(need)) {
|
||||
return String.format("Wrong selection: need %s, but get %s",
|
||||
need,
|
||||
selected
|
||||
);
|
||||
}
|
||||
|
||||
// all fine
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear() {
|
||||
super.onClear();
|
||||
this.selectedValues = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TargetTestableResult extends BaseTestableResult {
|
||||
|
||||
Target target = null;
|
||||
|
||||
boolean aiAssertEnabled = false;
|
||||
boolean aiAssertResStatus = false;
|
||||
int aiAssertTargetsCount = 0;
|
||||
|
||||
public void onFinish(String resDebugSource, boolean status, List<String> info, Target target) {
|
||||
this.onFinish(resDebugSource, status, info);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResAssert() {
|
||||
if (!this.aiAssertEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// not finished
|
||||
if (this.target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// wrong choose
|
||||
if (this.getResStatus() != this.aiAssertResStatus) {
|
||||
return String.format("Wrong status: need %s, but get %s",
|
||||
this.aiAssertResStatus,
|
||||
this.getResStatus()
|
||||
);
|
||||
}
|
||||
|
||||
// wrong targets
|
||||
if (this.target.getTargets().size() != this.aiAssertTargetsCount) {
|
||||
return String.format("Wrong targets count: need %d, but get %d",
|
||||
this.aiAssertTargetsCount,
|
||||
this.target.getTargets().size()
|
||||
);
|
||||
}
|
||||
|
||||
// all fine
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear() {
|
||||
super.onClear();
|
||||
this.target = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,6 @@ import mage.abilities.Ability;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
* <p>
|
||||
|
|
@ -17,7 +15,11 @@ import java.util.List;
|
|||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
interface TestableDialog {
|
||||
public interface TestableDialog {
|
||||
|
||||
void setRegNumber(Integer regNumber);
|
||||
|
||||
Integer getRegNumber();
|
||||
|
||||
String getGroup();
|
||||
|
||||
|
|
@ -25,7 +27,20 @@ interface TestableDialog {
|
|||
|
||||
String getDescription();
|
||||
|
||||
List<String> showDialog(Player player, Ability source, Game game, Player opponent);
|
||||
TestableResult getResult();
|
||||
|
||||
void showResult(Player player, Game game, String result);
|
||||
/**
|
||||
* Prepare dialog before show, e.g. clear prev results
|
||||
*/
|
||||
void prepare();
|
||||
|
||||
/**
|
||||
* Show game dialog to the user and save result
|
||||
*/
|
||||
void showDialog(Player player, Ability source, Game game, Player opponent);
|
||||
|
||||
/**
|
||||
* Show result dialog to the user
|
||||
*/
|
||||
void showResult(Player player, Game game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +57,7 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public class TestableDialogsRunner {
|
||||
|
||||
private final List<TestableDialog> dialogs = new ArrayList<>();
|
||||
private final Map<Integer, TestableDialog> dialogs = new LinkedHashMap<>();
|
||||
|
||||
static final int LAST_SELECTED_GROUP_ID = 997;
|
||||
static final int LAST_SELECTED_DIALOG_ID = 998;
|
||||
|
|
@ -79,12 +81,14 @@ public class TestableDialogsRunner {
|
|||
}
|
||||
|
||||
void registerDialog(TestableDialog dialog) {
|
||||
this.dialogs.add(dialog);
|
||||
Integer regNumber = this.dialogs.size() + 1;
|
||||
dialog.setRegNumber(regNumber);
|
||||
this.dialogs.put(regNumber, dialog);
|
||||
}
|
||||
|
||||
public void selectAndShowTestableDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
// select group or fast links
|
||||
List<String> groups = this.dialogs.stream()
|
||||
List<String> groups = this.dialogs.values().stream()
|
||||
.map(TestableDialog::getGroup)
|
||||
.distinct()
|
||||
.sorted()
|
||||
|
|
@ -129,8 +133,9 @@ public class TestableDialogsRunner {
|
|||
// all fine, can show it and finish
|
||||
lastSelectedGroup = needGroup;
|
||||
lastSelectedDialog = needDialog;
|
||||
List<String> resInfo = needDialog.showDialog(player, source, game, opponent);
|
||||
needDialog.showResult(player, game, String.join("<br>", resInfo));
|
||||
needDialog.prepare();
|
||||
needDialog.showDialog(player, source, game, opponent);
|
||||
needDialog.showResult(player, game);
|
||||
}
|
||||
|
||||
private Choice prepareSelectGroupChoice(List<String> groups) {
|
||||
|
|
@ -199,5 +204,9 @@ public class TestableDialogsRunner {
|
|||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
public Collection<TestableDialog> getDialogs() {
|
||||
return this.dialogs.values();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs, must contain dialogs result
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public interface TestableResult {
|
||||
|
||||
/**
|
||||
* Get source code line with called dialog, use it as starting debug point
|
||||
*/
|
||||
String getResDebugSource();
|
||||
|
||||
/**
|
||||
* Dialog's result
|
||||
*/
|
||||
boolean getResStatus();
|
||||
|
||||
/**
|
||||
* Dialog's detail result
|
||||
*/
|
||||
List<String> getResDetails();
|
||||
|
||||
/**
|
||||
* Save new result after show dialog
|
||||
*/
|
||||
void onFinish(String chooseDebugSource, boolean resStatus, List<String> resDetails);
|
||||
|
||||
boolean isFinished();
|
||||
|
||||
void onClear();
|
||||
|
||||
/**
|
||||
* Assert dialog result
|
||||
* - null - not ready (dev must setup wanted result)
|
||||
* - empty - good
|
||||
* - not empty - fail (return error message)
|
||||
*/
|
||||
String getResAssert();
|
||||
}
|
||||
|
|
@ -67,6 +67,7 @@ public class GameView implements Serializable {
|
|||
// TODO: implement and support in admin tools
|
||||
private int totalErrorsCount;
|
||||
private int totalEffectsCount;
|
||||
private int gameCycle;
|
||||
|
||||
public GameView(GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) {
|
||||
Player createdForPlayer = null;
|
||||
|
|
@ -214,6 +215,7 @@ public class GameView implements Serializable {
|
|||
this.rollbackTurnsAllowed = game.getOptions().rollbackTurnsAllowed;
|
||||
this.totalErrorsCount = game.getTotalErrorsCount();
|
||||
this.totalEffectsCount = game.getTotalEffectsCount();
|
||||
this.gameCycle = game.getState().getApplyEffectsCounter();
|
||||
}
|
||||
|
||||
private void checkPaid(UUID uuid, StackAbility stackAbility) {
|
||||
|
|
@ -358,4 +360,8 @@ public class GameView implements Serializable {
|
|||
public int getTotalEffectsCount() {
|
||||
return this.totalEffectsCount;
|
||||
}
|
||||
|
||||
public int getGameCycle() {
|
||||
return this.gameCycle;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,6 @@ public class Standard extends Constructed {
|
|||
super("Constructed - Standard");
|
||||
|
||||
setCodes.addAll(makeLegalSets());
|
||||
|
||||
banned.add("The Meathook Massacre");
|
||||
banned.add("Fable of the Mirror-Breaker");
|
||||
banned.add("Reckoner Bankbuster");
|
||||
banned.add("Invoke Despair");
|
||||
}
|
||||
|
||||
static List<String> makeLegalSets() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import mage.game.stack.StackAbility;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.player.ai.ma.optimizers.TreeOptimizer;
|
||||
import mage.player.ai.ma.optimizers.impl.*;
|
||||
import mage.player.ai.score.GameStateEvaluator2;
|
||||
import mage.player.ai.util.CombatInfo;
|
||||
import mage.player.ai.util.CombatUtil;
|
||||
import mage.players.Player;
|
||||
|
|
@ -399,7 +400,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
if (effect != null
|
||||
&& stackObject.getControllerId().equals(playerId)) {
|
||||
Target target = effect.getTarget();
|
||||
if (!target.isChoiceCompleted(game)) {
|
||||
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game)) {
|
||||
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
||||
Game sim = game.createSimulationForAI();
|
||||
StackAbility newAbility = (StackAbility) stackObject.copy();
|
||||
|
|
@ -848,10 +849,12 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
if (targets.isEmpty()) {
|
||||
return super.chooseTarget(outcome, cards, target, source, game);
|
||||
}
|
||||
if (!target.isChoiceCompleted(game)) {
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
for (UUID targetId : targets) {
|
||||
target.addTarget(targetId, source, game);
|
||||
if (target.isChoiceCompleted(game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
targets.clear();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -866,10 +869,12 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
if (targets.isEmpty()) {
|
||||
return super.choose(outcome, cards, target, source, game);
|
||||
}
|
||||
if (!target.isChoiceCompleted(game)) {
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
for (UUID targetId : targets) {
|
||||
target.add(targetId, game);
|
||||
if (target.isChoiceCompleted(game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
targets.clear();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.player.ai;
|
|||
import mage.abilities.Ability;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.game.Game;
|
||||
import mage.player.ai.score.GameStateEvaluator2;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.Date;
|
||||
|
|
@ -111,8 +112,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
|
|||
|
||||
protected void calculateActions(Game game) {
|
||||
if (!getNextAction(game)) {
|
||||
//logger.info("--- calculating possible actions for " + this.getName() + " on " + game.toString());
|
||||
Date startTime = new Date();
|
||||
currentScore = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
Game sim = createSimulation(game);
|
||||
SimulationNode2.resetCount();
|
||||
|
|
@ -145,15 +144,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
|
|||
} else {
|
||||
logger.info('[' + game.getPlayer(playerId).getName() + "][pre] Action: skip");
|
||||
}
|
||||
Date endTime = new Date();
|
||||
this.setLastThinkTime((endTime.getTime() - startTime.getTime()));
|
||||
|
||||
/*
|
||||
logger.warn("Last think time: " + this.getLastThinkTime()
|
||||
+ "; actions: " + actions.size()
|
||||
+ "; hand: " + this.getHand().size()
|
||||
+ "; permanents: " + game.getBattlefield().getAllPermanents().size());
|
||||
*/
|
||||
} else {
|
||||
logger.debug("Next Action exists!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.game.turn.CombatDamageStep;
|
||||
import mage.game.turn.EndOfCombatStep;
|
||||
import mage.game.turn.Step;
|
||||
import mage.player.ai.GameStateEvaluator2;
|
||||
import mage.player.ai.score.GameStateEvaluator2;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,152 @@
|
|||
package mage.player.ai;
|
||||
|
||||
import mage.MageItem;
|
||||
import mage.MageObject;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.player.ai.score.GameStateEvaluator2;
|
||||
import mage.players.PlayableObjectsList;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* AI related code - compare and sort possible targets due target/effect type
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PossibleTargetsComparator {
|
||||
|
||||
UUID abilityControllerId;
|
||||
Game game;
|
||||
PlayableObjectsList playableItems = new PlayableObjectsList();
|
||||
|
||||
public PossibleTargetsComparator(UUID abilityControllerId, Game game) {
|
||||
this.abilityControllerId = abilityControllerId;
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public void findPlayableItems() {
|
||||
this.playableItems = this.game.getPlayer(this.abilityControllerId).getPlayableObjects(this.game, Zone.ALL);
|
||||
}
|
||||
|
||||
private int getScoreFromBattlefield(MageItem item) {
|
||||
if (item instanceof Permanent) {
|
||||
// use battlefield score instead simple life
|
||||
return GameStateEvaluator2.evaluatePermanent((Permanent) item, game, false);
|
||||
} else {
|
||||
return getScoreFromLife(item);
|
||||
}
|
||||
}
|
||||
|
||||
private String getName(MageItem item) {
|
||||
if (item instanceof Player) {
|
||||
return ((Player) item).getName();
|
||||
} else if (item instanceof MageObject) {
|
||||
return ((MageObject) item).getName();
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static int getLifeForDamage(MageItem item, Game game) {
|
||||
int res = 0;
|
||||
if (item instanceof Player) {
|
||||
res = ((Player) item).getLife();
|
||||
} else if (item instanceof Card) {
|
||||
Card card = (Card) item;
|
||||
if (card.isPlaneswalker(game)) {
|
||||
res = card.getCounters(game).getCount(CounterType.LOYALTY);
|
||||
} else if (card.isBattle(game)) {
|
||||
res = card.getCounters(game).getCount(CounterType.DEFENSE);
|
||||
} else {
|
||||
int damage = 0;
|
||||
if (card instanceof Permanent) {
|
||||
damage = ((Permanent) card).getDamage();
|
||||
}
|
||||
res = Math.max(0, card.getToughness().getValue() - damage);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private int getScoreFromLife(MageItem item) {
|
||||
// TODO: replace permanent/card life by battlefield score?
|
||||
int res = getLifeForDamage(item, game);
|
||||
if (res == 0 && item instanceof Card) {
|
||||
res = ((Card) item).getManaValue();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean isMyItem(MageItem item) {
|
||||
return PossibleTargetsSelector.isMyItem(this.abilityControllerId, item);
|
||||
}
|
||||
|
||||
// sort by name-id at the end, so AI will use same choices in all simulations
|
||||
private final Comparator<MageItem> BY_NAME = (o1, o2) -> getName(o2).compareTo(getName(o1));
|
||||
private final Comparator<MageItem> BY_ID = Comparator.comparing(MageItem::getId);
|
||||
|
||||
private final Comparator<MageItem> BY_ME = (o1, o2) -> Boolean.compare(
|
||||
isMyItem(o2),
|
||||
isMyItem(o1)
|
||||
);
|
||||
|
||||
private final Comparator<MageItem> BY_BIGGER_SCORE = (o1, o2) -> Integer.compare(
|
||||
getScoreFromBattlefield(o2),
|
||||
getScoreFromBattlefield(o1)
|
||||
);
|
||||
|
||||
private final Comparator<MageItem> BY_PLAYABLE = (o1, o2) -> Boolean.compare(
|
||||
this.playableItems.containsObject(o2.getId()),
|
||||
this.playableItems.containsObject(o1.getId())
|
||||
);
|
||||
|
||||
private final Comparator<MageItem> BY_LAND = (o1, o2) -> {
|
||||
boolean isLand1 = o1 instanceof MageObject && ((MageObject) o1).isLand(game);
|
||||
boolean isLand2 = o2 instanceof MageObject && ((MageObject) o2).isLand(game);
|
||||
return Boolean.compare(isLand2, isLand1);
|
||||
};
|
||||
|
||||
private final Comparator<MageItem> BY_TYPE_PLAYER = (o1, o2) -> Boolean.compare(
|
||||
o2 instanceof Player,
|
||||
o1 instanceof Player
|
||||
);
|
||||
|
||||
private final Comparator<MageItem> BY_TYPE_PLANESWALKER = (o1, o2) -> {
|
||||
boolean isPlaneswalker1 = o1 instanceof MageObject && ((MageObject) o1).isPlaneswalker(game);
|
||||
boolean isPlaneswalker2 = o2 instanceof MageObject && ((MageObject) o2).isPlaneswalker(game);
|
||||
return Boolean.compare(isPlaneswalker2, isPlaneswalker1);
|
||||
};
|
||||
|
||||
private final Comparator<MageItem> BY_TYPE_BATTLE = (o1, o2) -> {
|
||||
boolean isBattle1 = o1 instanceof MageObject && ((MageObject) o1).isBattle(game);
|
||||
boolean isBattle2 = o2 instanceof MageObject && ((MageObject) o2).isBattle(game);
|
||||
return Boolean.compare(isBattle2, isBattle1);
|
||||
};
|
||||
|
||||
private final Comparator<MageItem> BY_TYPES = BY_TYPE_PLANESWALKER
|
||||
.thenComparing(BY_TYPE_BATTLE)
|
||||
.thenComparing(BY_TYPE_PLAYER);
|
||||
|
||||
/**
|
||||
* Default sorting for good effects - put the biggest items to the top
|
||||
*/
|
||||
public final Comparator<MageItem> ANY_MOST_VALUABLE_FIRST = BY_TYPES
|
||||
.thenComparing(BY_BIGGER_SCORE)
|
||||
.thenComparing(BY_NAME)
|
||||
.thenComparing(BY_ID);
|
||||
public final Comparator<MageItem> ANY_MOST_VALUABLE_LAST = ANY_MOST_VALUABLE_FIRST.reversed();
|
||||
|
||||
/**
|
||||
* Sorting for discard effects - put the biggest unplayable at the top, lands at the end anyway
|
||||
*/
|
||||
public final Comparator<MageItem> ANY_UNPLAYABLE_AND_USELESS = BY_LAND.reversed()
|
||||
.thenComparing(BY_PLAYABLE.reversed())
|
||||
.thenComparing(ANY_MOST_VALUABLE_FIRST);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
package mage.player.ai;
|
||||
|
||||
import mage.MageItem;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.ControllableOrOwnerable;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCardInGraveyardBattlefieldOrStack;
|
||||
import mage.target.common.TargetDiscard;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* AI related code - find possible targets and sort it due priority
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PossibleTargetsSelector {
|
||||
|
||||
Outcome outcome;
|
||||
Target target;
|
||||
UUID abilityControllerId;
|
||||
Ability source;
|
||||
Game game;
|
||||
|
||||
PossibleTargetsComparator comparators;
|
||||
|
||||
// possible targets lists
|
||||
List<MageItem> me = new ArrayList<>();
|
||||
List<MageItem> opponents = new ArrayList<>();
|
||||
List<MageItem> any = new ArrayList<>(); // for outcomes with any target like copy
|
||||
|
||||
public PossibleTargetsSelector(Outcome outcome, Target target, UUID abilityControllerId, Ability source, Game game) {
|
||||
this.outcome = outcome;
|
||||
this.target = target;
|
||||
this.abilityControllerId = abilityControllerId;
|
||||
this.source = source;
|
||||
this.game = game;
|
||||
this.comparators = new PossibleTargetsComparator(abilityControllerId, game);
|
||||
}
|
||||
|
||||
public void findNewTargets(Set<UUID> fromTargetsList) {
|
||||
// collect new valid targets
|
||||
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game).stream()
|
||||
.filter(id -> !target.contains(id))
|
||||
.filter(id -> fromTargetsList == null || fromTargetsList.contains(id))
|
||||
.filter(id -> target.canTarget(abilityControllerId, id, source, game))
|
||||
.map(id -> {
|
||||
Player player = game.getPlayer(id);
|
||||
if (player != null) {
|
||||
return player;
|
||||
} else {
|
||||
return game.getObject(id);
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// split targets between me and opponents
|
||||
found.forEach(item -> {
|
||||
if (isMyItem(abilityControllerId, item)) {
|
||||
this.me.add(item);
|
||||
} else {
|
||||
this.opponents.add(item);
|
||||
}
|
||||
this.any.add(item);
|
||||
});
|
||||
|
||||
if (target instanceof TargetDiscard) {
|
||||
// sort due unplayable
|
||||
sortByUnplayableAndUseless();
|
||||
} else {
|
||||
// sort due good/bad outcome
|
||||
sortByMostValuableTargets();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting for any good/bad effects
|
||||
*/
|
||||
private void sortByMostValuableTargets() {
|
||||
if (isGoodEffect()) {
|
||||
// for good effect must choose the biggest objects
|
||||
this.me.sort(comparators.ANY_MOST_VALUABLE_FIRST);
|
||||
this.opponents.sort(comparators.ANY_MOST_VALUABLE_LAST);
|
||||
this.any.sort(comparators.ANY_MOST_VALUABLE_FIRST);
|
||||
} else {
|
||||
// for bad effect must choose the smallest objects
|
||||
this.me.sort(comparators.ANY_MOST_VALUABLE_LAST);
|
||||
this.opponents.sort(comparators.ANY_MOST_VALUABLE_FIRST);
|
||||
this.any.sort(comparators.ANY_MOST_VALUABLE_LAST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting for discard
|
||||
*/
|
||||
private void sortByUnplayableAndUseless() {
|
||||
// used
|
||||
// no good or bad effect - you must choose
|
||||
comparators.findPlayableItems();
|
||||
this.me.sort(comparators.ANY_UNPLAYABLE_AND_USELESS);
|
||||
this.opponents.sort(comparators.ANY_UNPLAYABLE_AND_USELESS);
|
||||
this.any.sort(comparators.ANY_UNPLAYABLE_AND_USELESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Priority targets. Try to use as much as possible.
|
||||
*/
|
||||
public List<MageItem> getGoodTargets() {
|
||||
if (isAnyEffect()) {
|
||||
return this.any;
|
||||
}
|
||||
|
||||
if (isGoodEffect()) {
|
||||
return this.me;
|
||||
} else {
|
||||
return this.opponents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional targets. Try to ignore bad targets (e.g. opponent's creatures for your good effect).
|
||||
*/
|
||||
public List<MageItem> getBadTargets() {
|
||||
if (isAnyEffect()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (isGoodEffect()) {
|
||||
return this.opponents;
|
||||
} else {
|
||||
return this.me;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
||||
if (item instanceof Player) {
|
||||
return item.getId().equals(abilityControllerId);
|
||||
} else if (item instanceof ControllableOrOwnerable) {
|
||||
return ((ControllableOrOwnerable) item).getControllerOrOwnerId().equals(abilityControllerId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAnyEffect() {
|
||||
boolean isAnyEffect = outcome.anyTargetHasSameValue();
|
||||
|
||||
if (hasGoodExile()) {
|
||||
isAnyEffect = true;
|
||||
}
|
||||
|
||||
return isAnyEffect;
|
||||
}
|
||||
|
||||
private boolean isGoodEffect() {
|
||||
boolean isGoodEffect = outcome.isGood();
|
||||
|
||||
if (hasGoodExile()) {
|
||||
isGoodEffect = true;
|
||||
}
|
||||
|
||||
return isGoodEffect;
|
||||
}
|
||||
|
||||
private boolean hasGoodExile() {
|
||||
// exile workaround: exile is bad, but exile from library or graveyard in most cases is good
|
||||
// (more exiled -- more good things you get, e.g. delve's pay or search cards with same name)
|
||||
if (outcome == Outcome.Exile) {
|
||||
if (Zone.GRAVEYARD.match(target.getZone())
|
||||
|| Zone.LIBRARY.match(target.getZone())) {
|
||||
// TargetCardInGraveyardBattlefieldOrStack - used for additional payment like Craft, so do not allow big cards for it
|
||||
if (!(target instanceof TargetCardInGraveyardBattlefieldOrStack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasAnyTargets() {
|
||||
return !this.any.isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package mage.player.ai.ma;
|
||||
package mage.player.ai.score;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package mage.player.ai;
|
||||
package mage.player.ai.score;
|
||||
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.player.ai.ma.ArtificialScoringSystem;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package mage.player.ai.ma;
|
||||
package mage.player.ai.score;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.keyword.*;
|
||||
|
|
@ -7,6 +7,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO: outdated, replace by edh or commander brackets ability score
|
||||
* @author nantuko
|
||||
*/
|
||||
public final class MagicAbility {
|
||||
|
|
@ -18,10 +19,10 @@ public final class MagicAbility {
|
|||
put(DoubleStrikeAbility.getInstance().getRule(), 100);
|
||||
put(new ExaltedAbility().getRule(), 10);
|
||||
put(FirstStrikeAbility.getInstance().getRule(), 50);
|
||||
put(FlashAbility.getInstance().getRule(), 0);
|
||||
put(FlashAbility.getInstance().getRule(), 20);
|
||||
put(FlyingAbility.getInstance().getRule(), 50);
|
||||
put(new ForestwalkAbility().getRule(), 10);
|
||||
put(HasteAbility.getInstance().getRule(), 0);
|
||||
put(HasteAbility.getInstance().getRule(), 20);
|
||||
put(IndestructibleAbility.getInstance().getRule(), 150);
|
||||
put(InfectAbility.getInstance().getRule(), 60);
|
||||
put(IntimidateAbility.getInstance().getRule(), 50);
|
||||
|
|
@ -46,7 +47,7 @@ public final class MagicAbility {
|
|||
if (!scores.containsKey(ability.getRule())) {
|
||||
//System.err.println("Couldn't find ability score: " + ability.getClass().getSimpleName() + " - " + ability.toString());
|
||||
//TODO: add handling protection from ..., levelup, kicker, etc. abilities
|
||||
return 0;
|
||||
return 2; // more abilities - more score in any use cases
|
||||
}
|
||||
return scores.get(ability.getRule());
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
|
||||
package mage.player.ai.simulators;
|
||||
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.player.ai.ComputerPlayer;
|
||||
import mage.player.ai.PermanentEvaluator;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class ActionSimulator {
|
||||
|
||||
private ComputerPlayer player;
|
||||
private List<Card> playableInstants = new ArrayList<>();
|
||||
private List<ActivatedAbility> playableAbilities = new ArrayList<>();
|
||||
|
||||
private Game game;
|
||||
|
||||
public ActionSimulator(ComputerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void simulate(Game game) {
|
||||
|
||||
}
|
||||
|
||||
public int evaluateState() {
|
||||
// must find all leaved opponents
|
||||
Player opponent = game.getPlayer(game.getOpponents(player.getId(), false).stream().findFirst().orElse(null));
|
||||
if (opponent == null) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (game.checkIfGameIsOver()) {
|
||||
if (player.hasLost() || opponent.hasWon()) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
if (opponent.hasLost() || player.hasWon()) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
int value = player.getLife();
|
||||
value -= opponent.getLife();
|
||||
PermanentEvaluator evaluator = new PermanentEvaluator();
|
||||
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(player.getId())) {
|
||||
value += evaluator.evaluate(permanent, game);
|
||||
}
|
||||
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(player.getId())) {
|
||||
value -= evaluator.evaluate(permanent, game);
|
||||
}
|
||||
value += player.getHand().size();
|
||||
value -= opponent.getHand().size();
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
package mage.player.ai.simulators;
|
||||
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class CombatGroupSimulator implements Serializable {
|
||||
public List<CreatureSimulator> attackers = new ArrayList<>();
|
||||
public List<CreatureSimulator> blockers = new ArrayList<>();
|
||||
public UUID defenderId;
|
||||
public boolean defenderIsPlaneswalker;
|
||||
public int unblockedDamage;
|
||||
private CreatureSimulator attacker;
|
||||
|
||||
public CombatGroupSimulator(UUID defenderId, List<UUID> attackers, List<UUID> blockers, Game game) {
|
||||
this.defenderId = defenderId;
|
||||
for (UUID attackerId: attackers) {
|
||||
Permanent permanent = game.getPermanent(attackerId);
|
||||
this.attackers.add(new CreatureSimulator(permanent));
|
||||
}
|
||||
for (UUID blockerId: blockers) {
|
||||
Permanent permanent = game.getPermanent(blockerId);
|
||||
this.blockers.add(new CreatureSimulator(permanent));
|
||||
}
|
||||
//NOTE: assumes no banding
|
||||
attacker = this.attackers.get(0);
|
||||
}
|
||||
|
||||
private boolean hasFirstOrDoubleStrike() {
|
||||
for (CreatureSimulator creature: attackers) {
|
||||
if (creature.hasDoubleStrike || creature.hasFirstStrike)
|
||||
return true;
|
||||
}
|
||||
for (CreatureSimulator creature: blockers) {
|
||||
if (creature.hasDoubleStrike || creature.hasFirstStrike)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canBlock(Permanent blocker, Game game) {
|
||||
return blocker.canBlock(attacker.id, game);
|
||||
}
|
||||
|
||||
public void simulateCombat(Game game) {
|
||||
unblockedDamage = 0;
|
||||
|
||||
if (hasFirstOrDoubleStrike())
|
||||
assignDamage(true, game);
|
||||
assignDamage(false, game);
|
||||
}
|
||||
|
||||
private void assignDamage(boolean first, Game game) {
|
||||
if (blockers.isEmpty()) {
|
||||
if (canDamage(attacker, first))
|
||||
unblockedDamage += attacker.power;
|
||||
}
|
||||
else if (blockers.size() == 1) {
|
||||
CreatureSimulator blocker = blockers.get(0);
|
||||
if (canDamage(attacker, first)) {
|
||||
if (attacker.hasTrample) {
|
||||
int lethalDamage = blocker.getLethalDamage(game);
|
||||
if (attacker.power > lethalDamage) {
|
||||
blocker.damage += lethalDamage;
|
||||
unblockedDamage += attacker.power - lethalDamage;
|
||||
}
|
||||
else {
|
||||
blocker.damage += attacker.power;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canDamage(blocker, first)) {
|
||||
attacker.damage += blocker.power;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int damage = attacker.power;
|
||||
for (CreatureSimulator blocker: blockers) {
|
||||
if (damage > 0 && canDamage(attacker, first)) {
|
||||
int lethalDamage = blocker.getLethalDamage(game);
|
||||
if (damage > lethalDamage) {
|
||||
blocker.damage += lethalDamage;
|
||||
damage -= lethalDamage;
|
||||
}
|
||||
else {
|
||||
blocker.damage += damage;
|
||||
damage = 0;
|
||||
}
|
||||
}
|
||||
if (canDamage(blocker, first)) {
|
||||
attacker.damage += blocker.power;
|
||||
}
|
||||
}
|
||||
if (damage > 0) {
|
||||
if (attacker.hasTrample) {
|
||||
unblockedDamage += damage;
|
||||
}
|
||||
else {
|
||||
blockers.get(0).damage += damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canDamage(CreatureSimulator creature, boolean first) {
|
||||
if (first && (creature.hasFirstStrike || creature.hasDoubleStrike))
|
||||
return true;
|
||||
if (!first && (!creature.hasFirstStrike || creature.hasDoubleStrike))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns 3 attacker survives blockers destroyed
|
||||
* returns 2 both destroyed
|
||||
* returns 1 both survive
|
||||
* returns 0 attacker destroyed blockers survive
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int evaluateCombat() {
|
||||
int survivingBlockers = 0;
|
||||
for (CreatureSimulator blocker: blockers) {
|
||||
if (blocker.damage < blocker.toughness)
|
||||
survivingBlockers++;
|
||||
}
|
||||
if (attacker.isDead()) {
|
||||
if (survivingBlockers > 0) {
|
||||
return 0;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
if (survivingBlockers > 0) {
|
||||
return 1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
package mage.player.ai.simulators;
|
||||
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class CombatSimulator implements Serializable {
|
||||
|
||||
public List<CombatGroupSimulator> groups = new ArrayList<>();
|
||||
public List<UUID> defenders = new ArrayList<>();
|
||||
public Map<UUID, Integer> playersLife = new HashMap<>();
|
||||
public Map<UUID, Integer> planeswalkerLoyalty = new HashMap<>();
|
||||
public UUID attackerId;
|
||||
public int rating = 0;
|
||||
|
||||
public static CombatSimulator load(Game game) {
|
||||
CombatSimulator simCombat = new CombatSimulator();
|
||||
for (CombatGroup group: game.getCombat().getGroups()) {
|
||||
simCombat.groups.add(new CombatGroupSimulator(group.getDefenderId(), group.getAttackers(), group.getBlockers(), game));
|
||||
}
|
||||
for (UUID defenderId: game.getCombat().getDefenders()) {
|
||||
simCombat.defenders.add(defenderId);
|
||||
Player player = game.getPlayer(defenderId);
|
||||
if (player != null) {
|
||||
simCombat.playersLife.put(defenderId, player.getLife());
|
||||
}
|
||||
else {
|
||||
Permanent permanent = game.getPermanent(defenderId);
|
||||
simCombat.planeswalkerLoyalty.put(defenderId, permanent.getCounters(game).getCount(CounterType.LOYALTY));
|
||||
}
|
||||
}
|
||||
return simCombat;
|
||||
}
|
||||
|
||||
public CombatSimulator() {}
|
||||
|
||||
public void clear() {
|
||||
groups.clear();
|
||||
defenders.clear();
|
||||
attackerId = null;
|
||||
}
|
||||
|
||||
public void simulate(Game game) {
|
||||
for (CombatGroupSimulator group: groups) {
|
||||
group.simulateCombat(game);
|
||||
}
|
||||
}
|
||||
|
||||
public int evaluate() {
|
||||
Map<UUID, Integer> damage = new HashMap<>();
|
||||
int result = 0;
|
||||
for (CombatGroupSimulator group: groups) {
|
||||
if (!damage.containsKey(group.defenderId)) {
|
||||
damage.put(group.defenderId, group.unblockedDamage);
|
||||
}
|
||||
else {
|
||||
damage.put(group.defenderId, damage.get(group.defenderId) + group.unblockedDamage);
|
||||
}
|
||||
}
|
||||
//check for lethal damage to player
|
||||
for (Entry<UUID, Integer> entry: playersLife.entrySet()) {
|
||||
if (damage.containsKey(entry.getKey()) && entry.getValue() <= damage.get(entry.getKey())) {
|
||||
//TODO: check for protection
|
||||
//NOTE: not applicable for mulitplayer games
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (CombatGroupSimulator group: groups) {
|
||||
result += group.evaluateCombat();
|
||||
}
|
||||
|
||||
rating = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package mage.player.ai.simulators;
|
||||
|
||||
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class CreatureSimulator implements Serializable {
|
||||
public UUID id;
|
||||
public int damage;
|
||||
public int power;
|
||||
public int toughness;
|
||||
public boolean hasFirstStrike;
|
||||
public boolean hasDoubleStrike;
|
||||
public boolean hasTrample;
|
||||
public Permanent permanent;
|
||||
|
||||
public CreatureSimulator(Permanent permanent) {
|
||||
this.id = permanent.getId();
|
||||
this.damage = permanent.getDamage();
|
||||
this.power = permanent.getPower().getValue();
|
||||
this.toughness = permanent.getToughness().getValue();
|
||||
this.hasDoubleStrike = permanent.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId());
|
||||
this.hasFirstStrike = permanent.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId());
|
||||
this.hasTrample = permanent.getAbilities().containsKey(TrampleAbility.getInstance().getId());
|
||||
this.permanent = permanent;
|
||||
}
|
||||
|
||||
public boolean isDead() {
|
||||
return damage >= toughness;
|
||||
}
|
||||
|
||||
public int getLethalDamage(Game game) {
|
||||
List<FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters();
|
||||
/*
|
||||
* for handling Zilortha, Strength Incarnate:
|
||||
* 2020-04-17
|
||||
* Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on.
|
||||
*/
|
||||
boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream()
|
||||
.anyMatch(filter -> filter.match(permanent, game));
|
||||
int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ?
|
||||
// Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it.
|
||||
Math.max(power, 1) : toughness;
|
||||
return Math.max(lethalDamageThreshold - damage, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -288,6 +288,16 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
|
||||
@Override
|
||||
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
|
||||
|
||||
// nothing to choose
|
||||
target.prepareAmount(source, game);
|
||||
if (target.getAmountRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (target.getMaxNumberOfTargets() == 0 && target.getMinNumberOfTargets() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
||||
if (possibleTargets.isEmpty()) {
|
||||
return !target.isRequired(source);
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting game priority for " + getId() + " [" + DebugUtil.getMethodNameWithSource(1) + ']');
|
||||
logger.debug("Setting game priority for " + getId() + " [" + DebugUtil.getMethodNameWithSource(1, "method") + ']');
|
||||
}
|
||||
game.getState().setPriorityPlayerId(getId());
|
||||
}
|
||||
|
|
@ -328,7 +328,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
while (loop) {
|
||||
// start waiting for next answer
|
||||
response.clear();
|
||||
response.setActiveAction(game, DebugUtil.getMethodNameWithSource(1));
|
||||
response.setActiveAction(game, DebugUtil.getMethodNameWithSource(1, "method"));
|
||||
game.resumeTimer(getTurnControlledBy());
|
||||
responseOpenedForAnswer = true;
|
||||
|
||||
|
|
@ -690,12 +690,8 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
// choose one or multiple permanents
|
||||
UUID abilityControllerId = playerId;
|
||||
if (target.getTargetController() != null
|
||||
&& target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
// choose one or multiple targets
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
if (options == null) {
|
||||
options = new HashMap<>();
|
||||
}
|
||||
|
|
@ -782,11 +778,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
}
|
||||
|
||||
// choose one or multiple targets
|
||||
UUID abilityControllerId = playerId;
|
||||
if (target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
Map<String, Serializable> options = new HashMap<>();
|
||||
|
||||
while (canRespond()) {
|
||||
|
|
@ -869,13 +861,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
UUID abilityControllerId;
|
||||
if (target.getTargetController() != null
|
||||
&& target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
} else {
|
||||
abilityControllerId = playerId;
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
while (canRespond()) {
|
||||
|
||||
|
|
@ -966,13 +952,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
UUID abilityControllerId;
|
||||
if (target.getTargetController() != null
|
||||
&& target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
} else {
|
||||
abilityControllerId = playerId;
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
while (canRespond()) {
|
||||
boolean required = target.isRequiredExplicitlySet() ? target.isRequired() : target.isRequired(source);
|
||||
|
|
@ -1042,14 +1022,20 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
// nothing to choose
|
||||
target.prepareAmount(source, game);
|
||||
if (target.getAmountRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (target.getMaxNumberOfTargets() == 0 && target.getMinNumberOfTargets() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID abilityControllerId = playerId;
|
||||
if (target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
int amountTotal = target.getAmountTotal(game, source);
|
||||
if (amountTotal == 0) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import mage.constants.Duration;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ public final class AbunaAcolyte extends CardImpl {
|
|||
Ability ability1 = new SimpleActivatedAbility(new PreventDamageToTargetEffect(Duration.EndOfTurn, 1), new TapSourceCost());
|
||||
ability1.addTarget(new TargetAnyTarget());
|
||||
Ability ability2 = new SimpleActivatedAbility(new PreventDamageToTargetEffect(Duration.EndOfTurn, 2), new TapSourceCost());
|
||||
ability2.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability2.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability1);
|
||||
this.addAbility(ability2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.constants.CardType;
|
|||
import mage.constants.ComparisonType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanentAmount;
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ public final class AbzanCharm extends CardImpl {
|
|||
|
||||
// Choose one -
|
||||
// *Exile target creature with power 3 or greater
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(FILTER));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER));
|
||||
this.getSpellAbility().addEffect(new ExileTargetEffect());
|
||||
|
||||
// *You draw two cards and you lose 2 life
|
||||
|
|
|
|||
|
|
@ -16,10 +16,13 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE;
|
||||
|
||||
/**
|
||||
* @author JRHerlehy
|
||||
*/
|
||||
|
|
@ -47,7 +50,7 @@ public final class AcademyJourneymage extends CardImpl {
|
|||
|
||||
// When Academy Journeymage enters the battlefield, return target creature an opponent controls to its owner's hand.
|
||||
ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect());
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
ability.addTarget(new TargetPermanent(FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
|
|
@ -11,8 +10,9 @@ import mage.constants.CardType;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class AccumulatedKnowledge extends CardImpl {
|
||||
|
|
@ -24,13 +24,13 @@ public final class AccumulatedKnowledge extends CardImpl {
|
|||
}
|
||||
|
||||
public AccumulatedKnowledge(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
|
||||
|
||||
|
||||
// Draw a card, then draw cards equal to the number of cards named Accumulated Knowledge in all graveyards.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
|
||||
Effect effect = new DrawCardSourceControllerEffect(new CardsInAllGraveyardsCount(filter));
|
||||
effect.setText(", then draw cards equal to the number of cards named {this} in all graveyards");
|
||||
effect.setText(", then draw cards equal to the number of cards named Accumulated Knowledge in all graveyards");
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,7 @@ public final class AcererakTheArchlich extends CardImpl {
|
|||
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandSourceEffect(true))
|
||||
.withInterveningIf(AcererakTheArchlichCondition.instance);
|
||||
ability.addEffect(new VentureIntoTheDungeonEffect().concatBy("and"));
|
||||
ability.addHint(CurrentDungeonHint.instance);
|
||||
ability.addHint(CompletedDungeonCondition.getHint());
|
||||
this.addAbility(ability, new CompletedDungeonWatcher());
|
||||
this.addAbility(ability.addHint(CurrentDungeonHint.instance).addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher());
|
||||
|
||||
// Whenever Acererak the Archlich attacks, for each opponent, you create a 2/2 black Zombie creature token unless that player sacrifices a creature.
|
||||
this.addAbility(new AttacksTriggeredAbility(new AcererakTheArchlichEffect()));
|
||||
|
|
@ -83,7 +81,7 @@ class AcererakTheArchlichEffect extends OneShotEffect {
|
|||
AcererakTheArchlichEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "for each opponent, you create a 2/2 black Zombie creature " +
|
||||
"token unless that player sacrifices a creature";
|
||||
"token unless that player sacrifices a creature of their choice";
|
||||
}
|
||||
|
||||
private AcererakTheArchlichEffect(final AcererakTheArchlichEffect effect) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ public final class AcesBaseballBat extends CardImpl {
|
|||
// Equip legendary creature (1)
|
||||
this.addAbility(new EquipAbility(
|
||||
Outcome.AddAbility, new GenericManaCost(1),
|
||||
new TargetControlledCreaturePermanent(filterLegendary), false
|
||||
new TargetPermanent(filterLegendary), false
|
||||
));
|
||||
|
||||
// Equip {3}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import mage.abilities.DelayedTriggeredAbility;
|
|||
import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
|
|
@ -39,7 +39,7 @@ public final class AcidicDagger extends CardImpl {
|
|||
// {4}, {tap}: Whenever target creature deals combat damage to a non-Wall creature this turn,
|
||||
// destroy that non-Wall creature. When the targeted creature leaves the battlefield this turn,
|
||||
// sacrifice Acidic Dagger. Activate this ability only before blockers are declared.
|
||||
Ability ability = new ConditionalActivatedAbility(
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(
|
||||
new CreateDelayedTriggeredAbilityEffect(new AcidicDaggerDestroyNonWallAbility()),
|
||||
new GenericManaCost(4),
|
||||
BeforeBlockersAreDeclaredCondition.instance);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,11 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
|
|
@ -21,7 +24,7 @@ public final class ActOfAggression extends CardImpl {
|
|||
|
||||
public ActOfAggression(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R/P}{R/P}");
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));
|
||||
this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature"));
|
||||
this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn."));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.abilities.common.AttacksAttachedTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
|
|
@ -9,26 +7,26 @@ import mage.abilities.dynamicvalue.DynamicValue;
|
|||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.hint.common.ArtifactYouControlHint;
|
||||
import mage.abilities.keyword.EquipAbility;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PutCards;
|
||||
import mage.constants.SubType;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PutCards;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sobiech
|
||||
*/
|
||||
public final class AdaptiveOmnitool extends CardImpl {
|
||||
|
||||
private final static DynamicValue artifactYouControlCount = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent());
|
||||
private final static Hint hint = new ValueHint("Artifacts you control", artifactYouControlCount);
|
||||
|
||||
public AdaptiveOmnitool(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
|
||||
|
|
@ -37,7 +35,7 @@ public final class AdaptiveOmnitool extends CardImpl {
|
|||
|
||||
// Equipped creature gets +1/+1 for each artifact you control.
|
||||
this.addAbility(
|
||||
new SimpleStaticAbility(new BoostEquippedEffect(artifactYouControlCount, artifactYouControlCount)).addHint(hint)
|
||||
new SimpleStaticAbility(new BoostEquippedEffect(artifactYouControlCount, artifactYouControlCount)).addHint(ArtifactYouControlHint.instance)
|
||||
);
|
||||
|
||||
// Whenever equipped creature attacks, look at the top six cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public final class AdaptiveTrainingPost extends CardImpl {
|
|||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.CHARGE.createInstance()),
|
||||
StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false
|
||||
).withInterveningIf(condition));
|
||||
).withInterveningIf(condition).withRuleTextReplacement(true));
|
||||
|
||||
// Remove three charge counters from this artifact: When you next cast an instant or sorcery spell this turn, copy it and you may choose new targets for the copy.
|
||||
this.addAbility(new SimpleActivatedAbility(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
|
|
@ -20,13 +19,15 @@ import mage.filter.predicate.Predicates;
|
|||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public final class AdmonitionAngel extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent("nonland permanent other than Admonition Angel");
|
||||
private static final FilterPermanent filter = new FilterPermanent("nonland permanent other than {this}");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import mage.constants.TargetController;
|
|||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +39,7 @@ public final class AdvocateOfTheBeast extends CardImpl {
|
|||
|
||||
// At the beginning of your end step, put a +1/+1 counter on target Beast creature you control.
|
||||
Ability ability = new BeginningOfEndStepTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
|
||||
Target target = new TargetCreaturePermanent(filter);
|
||||
Target target = new TargetPermanent(filter);
|
||||
ability.addTarget(target);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
|
@ -10,12 +9,12 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class AegisAutomaton extends CardImpl {
|
||||
|
|
@ -29,7 +28,7 @@ public final class AegisAutomaton extends CardImpl {
|
|||
|
||||
// {4}{W}: Return another target creature you control to its owner's hand.
|
||||
Ability ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new ManaCostsImpl<>("{4}{W}"));
|
||||
ability.addTarget(new TargetControlledCreaturePermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL));
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.constants.Zone;
|
|||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.filter.predicate.mageobject.ToughnessPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -37,7 +38,7 @@ public final class AegisOfTheMeek extends CardImpl {
|
|||
// {1}, {T}: Target 1/1 creature gets +1/+2 until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(1, 2, Duration.EndOfTurn), new ManaCostsImpl<>("{1}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +32,7 @@ public final class AerialPredation extends CardImpl {
|
|||
|
||||
|
||||
// Destroy target creature with flying. You gain 2 life.
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
||||
this.getSpellAbility().addEffect(new GainLifeEffect(2));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ enum AerialSurveyorCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
return "defending player controls more lands than you";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -42,7 +43,7 @@ public final class AerieOuphes extends CardImpl {
|
|||
// Sacrifice Aerie Ouphes: Aerie Ouphes deals damage equal to its power to target creature with flying.
|
||||
Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(SourcePermanentPowerValue.NOT_NEGATIVE)
|
||||
.setText("it deals damage equal to its power to target creature with flying"), new SacrificeSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Persist
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class AetherBarrierEffect extends SacrificeEffect {
|
|||
|
||||
AetherBarrierEffect() {
|
||||
super(new FilterPermanent("permanent to sacrifice"), 1, "that player");
|
||||
this.staticText = "that player sacrifices a permanent unless they pay {1}";
|
||||
this.staticText = "that player sacrifices a permanent of their choice unless they pay {1}";
|
||||
}
|
||||
|
||||
private AetherBarrierEffect(final AetherBarrierEffect effect) {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
import mage.constants.SubType;
|
||||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AethershieldArtificer extends CardImpl {
|
||||
|
|
@ -45,7 +45,7 @@ public final class AethershieldArtificer extends CardImpl {
|
|||
IndestructibleAbility.getInstance(),
|
||||
Duration.EndOfTurn
|
||||
).setText("and gains indestructible until end of turn"));
|
||||
ability.addTarget(new TargetControlledCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.filter.common.FilterAttackingOrBlockingCreature;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -28,7 +29,7 @@ public final class Aethertow extends CardImpl {
|
|||
|
||||
// Put target attacking or blocking creature on top of its owner's library.
|
||||
this.getSpellAbility().addEffect(new AethertowEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
|
||||
// Conspire
|
||||
this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE));
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
|
|
@ -32,7 +35,7 @@ public final class AffectionateIndrik extends CardImpl {
|
|||
"<i>(Each deals damage equal to its power to the other.)</i>"),
|
||||
true
|
||||
);
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
|
||||
ability.addTarget(new TargetPermanent(FILTER_CREATURE_YOU_DONT_CONTROL));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import mage.MageInt;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -13,9 +15,9 @@ import mage.constants.CardType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||
|
||||
|
|
@ -37,7 +39,10 @@ public final class AgadeemOccultist extends CardImpl {
|
|||
this.toughness = new MageInt(2);
|
||||
|
||||
// {tap}: Put target creature card from an opponent's graveyard onto the battlefield under your control if its converted mana cost is less than or equal to the number of Allies you control.
|
||||
this.addAbility(new SimpleActivatedAbility(new AgadeemOccultistEffect(), new TapSourceCost()));
|
||||
Ability ability = new SimpleActivatedAbility(new AgadeemOccultistEffect(), new TapSourceCost());
|
||||
ability.addTarget(new TargetCardInOpponentsGraveyard(new FilterCreatureCard("target creature card from an opponent's graveyard")));
|
||||
ability.addHint(new ValueHint("Allies you control", new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.ALLY))));
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +55,6 @@ public final class AgadeemOccultist extends CardImpl {
|
|||
return new AgadeemOccultist(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AgadeemOccultistEffect extends OneShotEffect {
|
||||
|
||||
AgadeemOccultistEffect() {
|
||||
|
|
@ -70,26 +74,12 @@ class AgadeemOccultistEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
int allycount = 0;
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) {
|
||||
if (permanent.hasSubtype(SubType.ALLY, game)) {
|
||||
allycount++;
|
||||
}
|
||||
}
|
||||
FilterCard filter = new FilterCard("creature card in an opponent's graveyard");
|
||||
filter.add(CardType.CREATURE.getPredicate());
|
||||
TargetCardInOpponentsGraveyard target = new TargetCardInOpponentsGraveyard(1, 1, filter);
|
||||
|
||||
if (controller != null) {
|
||||
if (target.canChoose(source.getControllerId(), source, game)
|
||||
&& controller.choose(Outcome.GainControl, target, source, game)) {
|
||||
if (!target.getTargets().isEmpty()) {
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
if (card.getManaValue() <= allycount) {
|
||||
return controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
}
|
||||
Card card = game.getCard(source.getFirstTarget());
|
||||
if (card != null) {
|
||||
int allycount = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.ALLY)).calculate(game, source, this);
|
||||
if (card.getManaValue() <= allycount) {
|
||||
return controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -27,7 +28,7 @@ public final class AgentOfAcquisitions extends CardImpl {
|
|||
|
||||
// TODO: Draft specific abilities not implemented
|
||||
// Draft Agent of Acquisitions face up.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Draft Agent of Acquisitions face up - not implemented.")));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Draft {this} face up - not implemented.")));
|
||||
|
||||
// Instead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn Agent of Acquisitions face down and you can’t draft cards for the rest of this draft round.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Instead of drafting a card from a booster pack, "
|
||||
|
|
|
|||
|
|
@ -5,11 +5,14 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
|
|
@ -21,7 +24,7 @@ public final class AggressiveInstinct extends CardImpl {
|
|||
// Target creature you control deals damage equal to its power to target creature you don't control.
|
||||
this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER_CREATURE_YOU_DONT_CONTROL));
|
||||
}
|
||||
|
||||
private AggressiveInstinct(final AggressiveInstinct card) {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK;
|
||||
|
||||
/**
|
||||
* @author FenrisulfrX
|
||||
*/
|
||||
|
|
@ -27,7 +30,7 @@ public final class AgonizingDemise extends CardImpl {
|
|||
|
||||
// Destroy target nonblack creature. It can't be regenerated.
|
||||
this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER_PERMANENT_CREATURE_NON_BLACK));
|
||||
|
||||
// If Agonizing Demise was kicked, it deals damage equal to that creature's power to the creature's controller.
|
||||
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import mage.game.stack.Spell;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
|
@ -90,7 +89,7 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (!event.getTargetId().equals(getSourceId())) {
|
||||
return false;
|
||||
}
|
||||
StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game);
|
||||
StackObject targetingObject = game.findTargetingStackObject(this.getId().toString(), event);
|
||||
if (targetingObject == null || targetingObject instanceof Spell) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class AidFromTheCowlEffect extends OneShotEffect {
|
|||
AidFromTheCowlEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "reveal the top card of your library. If it's a permanent card, " +
|
||||
"you may put it onto the battlefield. Otherwise, you may put that card on the bottom of your library";
|
||||
"you may put it onto the battlefield. Otherwise, you may put it on the bottom of your library";
|
||||
}
|
||||
|
||||
private AidFromTheCowlEffect(final AidFromTheCowlEffect effect) {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.common.ReturnToHandTargetEffect;
|
||||
import mage.constants.SubType;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
*/
|
||||
public final class AirCultElemental extends CardImpl {
|
||||
|
|
@ -38,7 +38,7 @@ public final class AirCultElemental extends CardImpl {
|
|||
|
||||
// Whirlwind — When Air-Cult Elemental enters the battlefield, return up to one other target creature to its owner's hand.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect());
|
||||
ability.addTarget(new TargetCreaturePermanent(0, 1, filter, false));
|
||||
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||
this.addAbility(ability.withFlavorWord("Whirlwind"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +39,7 @@ public final class AirServant extends CardImpl {
|
|||
this.toughness = new MageInt(3);
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{2}{U}"));
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import mage.target.common.TargetCreaturePermanent;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.constants.SagaChapter.CHAPTER_I;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
|
|
@ -45,8 +47,8 @@ public final class AjaniFellsTheGodsire extends CardImpl {
|
|||
|
||||
// I -- Exile target creature an opponent controls with power 3 or greater.
|
||||
sagaAbility.addChapterEffect(
|
||||
this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I,
|
||||
new ExileTargetEffect(), new TargetCreaturePermanent(filter)
|
||||
this, CHAPTER_I, CHAPTER_I,
|
||||
new ExileTargetEffect(), new TargetPermanent(filter)
|
||||
);
|
||||
|
||||
// II -- Create a 2/1 white Cat Warrior creature token, then put a vigilance counter on a creature you control.
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
|
||||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.OnEventTriggeredAbility;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public final class AjanisMantra extends CardImpl {
|
||||
|
||||
public AjanisMantra(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}");
|
||||
this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new GainLifeEffect(1), true));
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
|
||||
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new GainLifeEffect(1), true));
|
||||
}
|
||||
|
||||
private AjanisMantra(final AjanisMantra card) {
|
||||
|
|
@ -29,5 +27,4 @@ public final class AjanisMantra extends CardImpl {
|
|||
public AjanisMantra copy() {
|
||||
return new AjanisMantra(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ import mage.constants.CardType;
|
|||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_ANOTHER_TARGET_CREATURE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -40,7 +43,7 @@ public final class AkroanConscriptor extends CardImpl {
|
|||
effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn);
|
||||
effect.setText("It gains haste until end of turn");
|
||||
ability.addEffect(effect);
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE));
|
||||
ability.addTarget(new TargetPermanent(FILTER_ANOTHER_TARGET_CREATURE));
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.condition.common.MoreCardsInHandThanOpponentsCondition;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
|
|
@ -15,18 +15,14 @@ import mage.filter.common.FilterControlledPermanent;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class AkutaBornOfAsh extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filterSwamp = new FilterControlledPermanent("a Swamp");
|
||||
static {
|
||||
filterSwamp.add(SubType.SWAMP.getPredicate());
|
||||
}
|
||||
private static final FilterControlledPermanent filterSwamp = new FilterControlledPermanent(SubType.SWAMP, "a Swamp");
|
||||
|
||||
public AkutaBornOfAsh(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
|
||||
|
|
@ -37,9 +33,14 @@ public final class AkutaBornOfAsh extends CardImpl {
|
|||
this.addAbility(HasteAbility.getInstance());
|
||||
|
||||
// At the beginning of your upkeep, if you have more cards in hand than each opponent, you may sacrifice a Swamp. If you do, return Akuta, Born of Ash from your graveyard to the battlefield.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD,
|
||||
TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToBattlefieldEffect(), new SacrificeTargetCost(filterSwamp)),
|
||||
false).withInterveningIf(MoreCardsInHandThanOpponentsCondition.instance));
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
||||
Zone.GRAVEYARD, TargetController.YOU,
|
||||
new DoIfCostPaid(
|
||||
new ReturnSourceFromGraveyardToBattlefieldEffect()
|
||||
.setText("return {this} from your graveyard to the battlefield"),
|
||||
new SacrificeTargetCost(filterSwamp)
|
||||
), false
|
||||
).withInterveningIf(MoreCardsInHandThanOpponentsCondition.instance));
|
||||
}
|
||||
|
||||
private AkutaBornOfAsh(final AkutaBornOfAsh card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
|
|
@ -13,25 +11,27 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class AlabornVeteran extends CardImpl {
|
||||
|
||||
public AlabornVeteran(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.KNIGHT);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// {tap}: Target creature gets +2/+2 until end of turn. Activate this ability only during your turn, before attackers are declared.
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD,
|
||||
new BoostTargetEffect(2, 2, Duration.EndOfTurn), new TapSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance);
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(
|
||||
new BoostTargetEffect(2, 2, Duration.EndOfTurn),
|
||||
new TapSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance
|
||||
);
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,10 +42,13 @@ public final class AlaniaDivergentStorm extends CardImpl {
|
|||
// Whenever you cast a spell, if it's the first instant spell, the first sorcery spell, or the first Otter
|
||||
// spell other than Alania you've cast this turn, you may have target opponent draw a card. If you do, copy
|
||||
// that spell. You may choose new targets for the copy.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new DoIfCostPaid(new CopyTargetStackObjectEffect(true), new AlaniaDivergentStormCost()),
|
||||
Ability ability = new SpellCastControllerTriggeredAbility(
|
||||
new DoIfCostPaid(new CopyTargetStackObjectEffect(true).setText("copy that spell. You may choose new targets for the copy")
|
||||
, new AlaniaDivergentStormCost()),
|
||||
null, false, SetTargetPointer.SPELL
|
||||
).withInterveningIf(AlaniaDivergentStormCondition.instance), new AlaniaDivergentStormWatcher());
|
||||
).withInterveningIf(AlaniaDivergentStormCondition.instance);
|
||||
ability.addTarget(new TargetOpponent());
|
||||
this.addAbility(ability, new AlaniaDivergentStormWatcher());
|
||||
}
|
||||
|
||||
private AlaniaDivergentStorm(final AlaniaDivergentStorm card) {
|
||||
|
|
@ -58,12 +61,10 @@ public final class AlaniaDivergentStorm extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
// Based on MarathWillOfTheWildRemoveCountersCost
|
||||
class AlaniaDivergentStormCost extends CostImpl {
|
||||
|
||||
AlaniaDivergentStormCost() {
|
||||
this.text = "have target opponent draw a card";
|
||||
this.addTarget(new TargetOpponent());
|
||||
}
|
||||
|
||||
private AlaniaDivergentStormCost(AlaniaDivergentStormCost cost) {
|
||||
|
|
@ -73,32 +74,18 @@ class AlaniaDivergentStormCost extends CostImpl {
|
|||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
for (UUID opponentID : game.getOpponents(controllerId)) {
|
||||
Player opponent = game.getPlayer(opponentID);
|
||||
if (opponent == null) {
|
||||
continue;
|
||||
}
|
||||
if (opponent.canBeTargetedBy(source.getSourceObject(game), controllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
Player opponent = game.getPlayer(source.getFirstTarget());
|
||||
return player != null && opponent != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
this.getTargets().clearChosen();
|
||||
paid = false;
|
||||
if (this.getTargets().choose(Outcome.DrawCard, controllerId, source.getSourceId(), source, game)) {
|
||||
Player opponent = game.getPlayer(this.getTargets().getFirstTarget());
|
||||
if (opponent == null || !opponent.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
paid = opponent.drawCards(1, source, game) > 0;
|
||||
Player opponent = game.getPlayer(source.getFirstTarget());
|
||||
if (opponent == null || !opponent.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
paid = opponent.drawCards(1, source, game) > 0;
|
||||
return paid;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import mage.constants.Duration;
|
|||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +35,7 @@ public final class Alarum extends CardImpl {
|
|||
Effect effect = new BoostTargetEffect(1, 3, Duration.EndOfTurn);
|
||||
effect.setText("It gets +1/+3 until end of turn");
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
}
|
||||
|
||||
private Alarum(final Alarum card) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public final class AlchemistsTalent extends CardImpl {
|
|||
new SpellCastControllerTriggeredAbility(
|
||||
new DamagePlayersEffect(AlchemistsTalentValue.instance, TargetController.OPPONENT)
|
||||
.setText("{this} deals damage equal to that spell's mana value to each opponent"),
|
||||
StaticFilters.FILTER_SPELL, false, SetTargetPointer.SPELL
|
||||
StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL
|
||||
).withInterveningIf(AlchemistsTalentCondition.instance), 3
|
||||
)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.common.FilterBlockingCreature;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -21,7 +22,7 @@ public final class AlibansTower extends CardImpl {
|
|||
|
||||
// Target blocking creature gets +3/+1 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(3, 1, Duration.EndOfTurn));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterBlockingCreature()));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(new FilterBlockingCreature()));
|
||||
}
|
||||
|
||||
private AlibansTower(final AlibansTower card) {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -29,7 +32,7 @@ public final class AlluringSiren extends CardImpl {
|
|||
|
||||
// {T}: Target creature an opponent controls attacks you this turn if able.
|
||||
Ability ability = new SimpleActivatedAbility(new AttacksIfAbleTargetEffect(Duration.EndOfTurn, TargetController.YOU), new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
ability.addTarget(new TargetPermanent(FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward
|
||||
|
|
@ -27,7 +30,7 @@ public final class AlphaBrawl extends CardImpl {
|
|||
|
||||
// Target creature an opponent controls deals damage equal to its power to each other creature that player controls, then each of those creatures deals damage equal to its power to that creature.
|
||||
this.getSpellAbility().addEffect(new AlphaBrawlEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import mage.constants.Duration;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -37,7 +38,7 @@ public final class AlphaKavu extends CardImpl {
|
|||
// {1}{G}: Target Kavu creature gets -1/+1 until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(-1, 1, Duration.EndOfTurn),
|
||||
new ManaCostsImpl<>("{1}{G}"));
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
63
Mage.Sets/src/mage/cards/a/AlpharaelDreamingAcolyte.java
Normal file
63
Mage.Sets/src/mage/cards/a/AlpharaelDreamingAcolyte.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardControllerEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AlpharaelDreamingAcolyte extends CardImpl {
|
||||
|
||||
public AlpharaelDreamingAcolyte(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.CLERIC);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// When Alpharael enters, draw two cards. Then discard two cards unless you discard an artifact card.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(2));
|
||||
ability.addEffect(new DoIfCostPaid(
|
||||
null, new DiscardControllerEffect(2),
|
||||
new DiscardCardCost(StaticFilters.FILTER_CARD_ARTIFACT)
|
||||
.setText("discard an artifact card instead of discarding two cards")
|
||||
).setText("Then discard two cards unless you discard an artifact card"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// During your turn, Alpharael has deathtouch.
|
||||
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
|
||||
new GainAbilitySourceEffect(DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield),
|
||||
MyTurnCondition.instance, "during your turn, {this} has deathtouch"
|
||||
)));
|
||||
}
|
||||
|
||||
private AlpharaelDreamingAcolyte(final AlpharaelDreamingAcolyte card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlpharaelDreamingAcolyte copy() {
|
||||
return new AlpharaelDreamingAcolyte(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -17,24 +16,17 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*
|
||||
*/
|
||||
public final class AltarGolem extends CardImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control");
|
||||
|
||||
static {
|
||||
filter.add(TappedPredicate.UNTAPPED);
|
||||
}
|
||||
|
||||
public AltarGolem(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}");
|
||||
this.subtype.add(SubType.GOLEM);
|
||||
|
|
@ -53,7 +45,7 @@ public final class AltarGolem extends CardImpl {
|
|||
this.addAbility(new SimpleStaticAbility(new DontUntapInControllersUntapStepSourceEffect()));
|
||||
|
||||
// Tap five untapped creatures you control: Untap Altar Golem.
|
||||
this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(5, 5, filter, true))));
|
||||
this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new TapTargetCost(new TargetControlledPermanent(5, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES))));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ import mage.abilities.hint.common.CovenHint;
|
|||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.AbilityWord;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
|
|
@ -46,8 +49,7 @@ public final class AmbitiousFarmhand extends CardImpl {
|
|||
// Coven—{1}{W}{W}: Transform Ambitious Farmhand. Activate only if you control three or more creatures with different powers.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new ActivateIfConditionActivatedAbility(
|
||||
Zone.BATTLEFIELD, new TransformSourceEffect(),
|
||||
new ManaCostsImpl<>("{1}{W}{W}"), CovenCondition.instance
|
||||
new TransformSourceEffect(), new ManaCostsImpl<>("{1}{W}{W}"), CovenCondition.instance
|
||||
).setAbilityWord(AbilityWord.COVEN).addHint(CovenHint.instance));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ class AminatouUltimateEffect extends OneShotEffect {
|
|||
|
||||
AminatouUltimateEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "Choose left or right. Each player gains control of all nonland permanents other than Aminatou,"
|
||||
+ " the Fateshifter controlled by the next player in the chosen direction.";
|
||||
staticText = "Choose left or right. Each player gains control of all nonland permanents other than {this}"
|
||||
+ " controlled by the next player in the chosen direction.";
|
||||
}
|
||||
|
||||
private AminatouUltimateEffect(final AminatouUltimateEffect effect) {
|
||||
|
|
|
|||
94
Mage.Sets/src/mage/cards/a/AmyRose.java
Normal file
94
Mage.Sets/src/mage/cards/a/AmyRose.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterAttackingCreature;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.SecondTargetPointer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AmyRose extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterAttackingCreature("other attacking creature");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
public AmyRose(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HEDGEHOG);
|
||||
this.subtype.add(SubType.WARRIOR);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Haste
|
||||
this.addAbility(HasteAbility.getInstance());
|
||||
|
||||
// Whenever Amy Rose attacks, attach up to one target Equipment to her. Then up to one other target attacking creature gets +X/+0 until end of turn, where X is Amy Rose's power.
|
||||
Ability ability = new AttacksTriggeredAbility(new AmyRoseEffect());
|
||||
ability.addEffect(new BoostTargetEffect(
|
||||
SourcePermanentPowerValue.NOT_NEGATIVE, StaticValue.get(0)
|
||||
).setTargetPointer(new SecondTargetPointer())
|
||||
.setText("Then up to one other target attacking creature gets +X/+0 until end of turn, where X is {this}'s power"));
|
||||
ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_EQUIPMENT));
|
||||
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private AmyRose(final AmyRose card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmyRose copy() {
|
||||
return new AmyRose(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AmyRoseEffect extends OneShotEffect {
|
||||
|
||||
AmyRoseEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "attach up to one target Equipment to her";
|
||||
}
|
||||
|
||||
private AmyRoseEffect(final AmyRoseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmyRoseEffect copy() {
|
||||
return new AmyRoseEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
Permanent equipment = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
return permanent != null && equipment != null && permanent.addAttachment(equipment.getId(), source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import mage.filter.predicate.permanent.TappedPredicate;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ public final class AnaBattlemage extends CardImpl {
|
|||
// When Ana Battlemage enters the battlefield, if it was kicked with its {1}{B} kicker, tap target untapped creature and that creature deals damage equal to its power to its controller.
|
||||
ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()).withInterveningIf(condition2);
|
||||
ability.addEffect(new AnaBattlemageEffect());
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ class AnaBattlemageEffect extends OneShotEffect {
|
|||
|
||||
AnaBattlemageEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "and it deals damage equal to its power to its controller";
|
||||
this.staticText = "and that creature deals damage equal to its power to its controller";
|
||||
}
|
||||
|
||||
private AnaBattlemageEffect(final AnaBattlemageEffect effect) {
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SanctuaryInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.common.SanctuaryTriggeredAbility;
|
||||
import mage.abilities.effects.common.AddContinuousEffectToGame;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AnaSanctuary extends CardImpl {
|
||||
|
|
@ -27,10 +21,11 @@ public final class AnaSanctuary extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
||||
|
||||
// At the beginning of your upkeep, if you control a blue or black permanent, target creature gets +1/+1 until end of turn. If you control a blue permanent and a black permanent, that creature gets +5/+5 until end of turn instead.
|
||||
Ability ability = new SanctuaryInterveningIfTriggeredAbility(
|
||||
new BoostEffect(1), new BoostEffect(5), ObjectColor.BLACK, ObjectColor.BLUE,
|
||||
"At the beginning of your upkeep, if you control a blue or black permanent, "
|
||||
+ "target creature gets +1/+1 until end of turn. If you control a blue permanent and a black permanent, that creature gets +5/+5 until end of turn instead."
|
||||
Ability ability = new SanctuaryTriggeredAbility(
|
||||
new AddContinuousEffectToGame(new BoostTargetEffect(1, 1)),
|
||||
new AddContinuousEffectToGame(new BoostTargetEffect(5, 5)),
|
||||
ObjectColor.BLACK, ObjectColor.BLUE, "target creature gets +1/+1 until end of turn. " +
|
||||
"If you control a blue permanent and a black permanent, that creature gets +5/+5 until end of turn instead."
|
||||
);
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
|
|
@ -45,31 +40,3 @@ public final class AnaSanctuary extends CardImpl {
|
|||
return new AnaSanctuary(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BoostEffect extends OneShotEffect {
|
||||
|
||||
private final int amount;
|
||||
|
||||
BoostEffect(int amount) {
|
||||
super(Outcome.Benefit);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
private BoostEffect(final BoostEffect effect) {
|
||||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoostEffect copy() {
|
||||
return new BoostEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
ContinuousEffect effect = new BoostTargetEffect(amount, amount, Duration.EndOfTurn);
|
||||
effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game));
|
||||
game.addEffect(effect, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -39,7 +40,7 @@ public final class AnabaAncestor extends CardImpl {
|
|||
|
||||
// {T}: Another target Minotaur creature gets +1/+1 until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import mage.game.events.ZoneChangeEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ public final class AnafenzaTheForemost extends CardImpl {
|
|||
|
||||
// Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control.
|
||||
Ability ability = new AttacksTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
|
||||
ability.addTarget(new TargetControlledCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// If a nontoken creature an opponent owns would die or a creature card not on the battlefield would be put into an opponent's graveyard, exile that card instead.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
|
@ -19,9 +18,10 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Loki
|
||||
*/
|
||||
|
|
@ -49,9 +49,9 @@ public final class Anavolver extends CardImpl {
|
|||
|
||||
// If Anavolver was kicked with its {B} kicker, it enters with a +1/+1 counter on it and with "Pay 3 life: Regenerate Anavolver."
|
||||
EntersBattlefieldAbility ability2 = new EntersBattlefieldAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance(1),false), new KickedCostCondition("{B}"),
|
||||
"If {this} was kicked with its {B} kicker, it enters with a +1/+1 counter on it and with \"Pay 3 life: Regenerate Anavolver.\"",
|
||||
"{this} enters with a +1/+1 counter on it and with \"Pay 3 life: Regenerate Anavolver.\"");
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), new KickedCostCondition("{B}"),
|
||||
"If {this} was kicked with its {B} kicker, it enters with a +1/+1 counter on it and with \"Pay 3 life: Regenerate {this}.\"",
|
||||
"{this} enters with a +1/+1 counter on it and with \"Pay 3 life: Regenerate {this}.\"");
|
||||
((EntersBattlefieldEffect)ability2.getEffects().get(0)).addEffect(new GainAbilitySourceEffect(new SimpleActivatedAbility(new RegenerateSourceEffect(), new PayLifeCost(3)), Duration.WhileOnBattlefield));
|
||||
this.addAbility(ability2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.common.SourceAttackingCondition;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
|
@ -42,7 +40,9 @@ public final class AncientHellkite extends CardImpl {
|
|||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// {R}: Ancient Hellkite deals 1 damage to target creature defending player controls. Activate this ability only if Ancient Hellkite is attacking.
|
||||
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl<>("{R}"), SourceAttackingCondition.instance);
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(
|
||||
new DamageTargetEffect(1), new ManaCostsImpl<>("{R}"), SourceAttackingCondition.instance
|
||||
);
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_ANOTHER_TARGET_CREATURE;
|
||||
|
||||
/**
|
||||
* @author emerald000
|
||||
*/
|
||||
|
|
@ -42,14 +45,14 @@ public final class AngelOfCondemnation extends CardImpl {
|
|||
new ExileReturnBattlefieldNextEndStepTargetEffect(), new ManaCostsImpl<>("{2}{W}")
|
||||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE));
|
||||
ability.addTarget(new TargetPermanent(FILTER_ANOTHER_TARGET_CREATURE));
|
||||
this.addAbility(ability);
|
||||
|
||||
// {2}{W}, {T}, Exert Angel of Condemnation: Exile another target creature until Angel of Condemnation leaves the battlefield.
|
||||
ability = new SimpleActivatedAbility(new ExileUntilSourceLeavesEffect(), new ManaCostsImpl<>("{2}{W}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new ExertSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE));
|
||||
ability.addTarget(new TargetPermanent(FILTER_ANOTHER_TARGET_CREATURE));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.constants.Duration;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterAttackingOrBlockingCreature;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +36,7 @@ public final class AngelicPage extends CardImpl {
|
|||
|
||||
//{T}: Target attacking or blocking creature gets +1/+1 until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(new FilterAttackingOrBlockingCreature()));
|
||||
ability.addTarget(new TargetPermanent(new FilterAttackingOrBlockingCreature()));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@ package mage.cards.a;
|
|||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -47,7 +50,7 @@ class AngelsTrumpetTapEffect extends OneShotEffect {
|
|||
|
||||
AngelsTrumpetTapEffect() {
|
||||
super(Outcome.Tap);
|
||||
this.staticText = "tap all untapped creatures that player controls that didn't attack this turn. Angel's Trumpet deals damage to the player equal to the number of creatures tapped this way";
|
||||
this.staticText = "tap all untapped creatures that player controls that didn't attack this turn. {this} deals damage to the player equal to the number of creatures tapped this way";
|
||||
}
|
||||
|
||||
private AngelsTrumpetTapEffect(final AngelsTrumpetTapEffect effect) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.PreventAllDamageByAllPermanentsEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author shieldal
|
||||
*/
|
||||
public final class AngusMackenzie extends CardImpl {
|
||||
|
||||
public AngusMackenzie(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{W}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}{U}");
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.CLERIC);
|
||||
|
|
@ -30,14 +28,11 @@ public final class AngusMackenzie extends CardImpl {
|
|||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
Effect effect = new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true);
|
||||
effect.setText("Prevent all combat damage that would be dealt this turn");
|
||||
// {G}{W}{U}, {tap}: Prevent all combat damage that would be dealt this turn. Activate this ability only before the combat damage step.
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(
|
||||
Zone.BATTLEFIELD,
|
||||
effect,
|
||||
new ManaCostsImpl<>("{G}{W}{U}"),
|
||||
BeforeCombatDamageCondition.getInstance()
|
||||
new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true)
|
||||
.setText("Prevent all combat damage that would be dealt this turn"),
|
||||
new ManaCostsImpl<>("{G}{W}{U}"), BeforeCombatDamageCondition.getInstance()
|
||||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
|
@ -62,15 +57,15 @@ class BeforeCombatDamageCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
PhaseStep phaseStep = game.getTurnStepType();
|
||||
if(phaseStep.getIndex() < PhaseStep.FIRST_COMBAT_DAMAGE.getIndex()) {
|
||||
return true;
|
||||
}
|
||||
PhaseStep phaseStep = game.getTurnStepType();
|
||||
if (phaseStep.getIndex() < PhaseStep.FIRST_COMBAT_DAMAGE.getIndex()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "before the combat damage step";
|
||||
return "before the combat damage step";
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ public final class AnimateWall extends CardImpl {
|
|||
|
||||
|
||||
// Enchant Wall
|
||||
TargetPermanent auraTarget = new TargetCreaturePermanent(filter);
|
||||
TargetPermanent auraTarget = new TargetPermanent(filter);
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
|
||||
Ability ability = new EnchantAbility(auraTarget);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -20,7 +23,7 @@ public final class Annihilate extends CardImpl {
|
|||
|
||||
// Destroy target nonblack creature. It can't be regenerated.
|
||||
this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(FILTER_PERMANENT_CREATURE_NON_BLACK));
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.constants.CardType;
|
|||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -36,7 +37,7 @@ public final class AnointedDeacon extends CardImpl {
|
|||
// At the beginning of combat on your turn, you may have target Vampire get +2/+0 until end of turn.
|
||||
Ability ability = new BeginningOfCombatTriggeredAbility(
|
||||
new BoostTargetEffect(2, 0, Duration.EndOfTurn), true);
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
|
|
@ -61,12 +61,8 @@ class AnotherRoundEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
int xValue = GetXValue.instance.calculate(game, source, this);
|
||||
TargetControlledCreaturePermanent target =
|
||||
new TargetControlledCreaturePermanent(
|
||||
0, Integer.MAX_VALUE,
|
||||
StaticFilters.FILTER_CONTROLLED_CREATURE, true
|
||||
);
|
||||
|
||||
TargetPermanent target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE);
|
||||
target.withNotTarget(true);
|
||||
for (int i = 0; i <= xValue; ++i) {
|
||||
target.clearChosen();
|
||||
controller.chooseTarget(Outcome.Benefit, target, source, game);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
|
|
@ -41,7 +42,7 @@ public final class AntlerSkulkin extends CardImpl {
|
|||
|
||||
// {2}: Target white creature gains persist until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(new PersistAbility(), Duration.EndOfTurn), new ManaCostsImpl<>("{2}"));
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
|
|
@ -12,25 +10,27 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class ApprenticeSorcerer extends CardImpl {
|
||||
|
||||
public ApprenticeSorcerer(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// {tap}: Apprentice Sorcerer deals 1 damage to any target. Activate this ability only during your turn, before attackers are declared.
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD,
|
||||
new DamageTargetEffect(1), new TapSourceCost(), MyTurnBeforeAttackersDeclaredCondition.instance);
|
||||
Ability ability = new ActivateIfConditionActivatedAbility(
|
||||
new DamageTargetEffect(1), new TapSourceCost(),
|
||||
MyTurnBeforeAttackersDeclaredCondition.instance
|
||||
);
|
||||
ability.addTarget(new TargetAnyTarget());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect {
|
|||
ApproachOfTheSecondSunEffect() {
|
||||
super(Outcome.Win);
|
||||
this.staticText
|
||||
= "If this spell was cast from your hand and you've cast another spell named {this} this game, you win the game. "
|
||||
= "If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. "
|
||||
+ "Otherwise, put {this} into its owner's library seventh from the top and you gain 7 life.";
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue