mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Merge branch 'Card-Forge:master' into master
This commit is contained in:
@@ -1063,6 +1063,10 @@ public class ComputerUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cardState.hasKeyword(Keyword.EXALTED) || cardState.hasKeyword(Keyword.EXTORT)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (cardState.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) {
|
if (cardState.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) {
|
||||||
// Planning to choose Haste for Riot, so do this in Main 1
|
// Planning to choose Haste for Riot, so do this in Main 1
|
||||||
return true;
|
return true;
|
||||||
@@ -1070,6 +1074,7 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
// if we have non-persistent mana in our pool, would be good to try to use it and not waste it
|
// if we have non-persistent mana in our pool, would be good to try to use it and not waste it
|
||||||
if (ai.getManaPool().willManaBeLostAtEndOfPhase()) {
|
if (ai.getManaPool().willManaBeLostAtEndOfPhase()) {
|
||||||
|
// TODO should check if some will be kept and skip those
|
||||||
boolean canUseToPayCost = false;
|
boolean canUseToPayCost = false;
|
||||||
for (byte color : ManaAtom.MANATYPES) {
|
for (byte color : ManaAtom.MANATYPES) {
|
||||||
// tries to reuse any amount of colorless if cost only has generic
|
// tries to reuse any amount of colorless if cost only has generic
|
||||||
@@ -1089,10 +1094,6 @@ public class ComputerUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardState.hasKeyword(Keyword.EXALTED) || cardState.hasKeyword(Keyword.EXTORT)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
|
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
|
||||||
if (card.isEquipment()) {
|
if (card.isEquipment()) {
|
||||||
boolean playNow = false;
|
boolean playNow = false;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public class ComputerUtilMana {
|
|||||||
ability.setActivatingPlayer(card.getController(), true);
|
ability.setActivatingPlayer(card.getController(), true);
|
||||||
if (ability.isManaAbility()) {
|
if (ability.isManaAbility()) {
|
||||||
score += ability.calculateScoreForManaAbility();
|
score += ability.calculateScoreForManaAbility();
|
||||||
|
// TODO check TriggersWhenSpent
|
||||||
}
|
}
|
||||||
else if (!ability.isTrigger() && ability.isPossible()) {
|
else if (!ability.isTrigger() && ability.isPossible()) {
|
||||||
score += 13; //add 13 for any non-mana activated abilities
|
score += 13; //add 13 for any non-mana activated abilities
|
||||||
@@ -393,9 +394,9 @@ public class ComputerUtilMana {
|
|||||||
String manaProduced = toPay.isSnow() && hostCard.isSnow() ? "S" : GameActionUtil.generatedTotalMana(saPayment);
|
String manaProduced = toPay.isSnow() && hostCard.isSnow() ? "S" : GameActionUtil.generatedTotalMana(saPayment);
|
||||||
//String originalProduced = manaProduced;
|
//String originalProduced = manaProduced;
|
||||||
|
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromPlayer(ai);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(hostCard);
|
||||||
repParams.put(AbilityKey.Mana, manaProduced);
|
repParams.put(AbilityKey.Mana, manaProduced);
|
||||||
repParams.put(AbilityKey.Affected, hostCard);
|
repParams.put(AbilityKey.Activator, ai);
|
||||||
repParams.put(AbilityKey.AbilityMana, saPayment); // RootAbility
|
repParams.put(AbilityKey.AbilityMana, saPayment); // RootAbility
|
||||||
|
|
||||||
// TODO Damping Sphere might replace later?
|
// TODO Damping Sphere might replace later?
|
||||||
@@ -1614,9 +1615,9 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
// setup produce mana replacement effects
|
// setup produce mana replacement effects
|
||||||
String origin = mp.getOrigProduced();
|
String origin = mp.getOrigProduced();
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromPlayer(ai);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(sourceCard);
|
||||||
repParams.put(AbilityKey.Mana, origin);
|
repParams.put(AbilityKey.Mana, origin);
|
||||||
repParams.put(AbilityKey.Affected, sourceCard);
|
repParams.put(AbilityKey.Activator, ai);
|
||||||
repParams.put(AbilityKey.AbilityMana, m); // RootAbility
|
repParams.put(AbilityKey.AbilityMana, m); // RootAbility
|
||||||
|
|
||||||
List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
|
List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.*;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
@@ -52,27 +53,24 @@ public abstract class GameState {
|
|||||||
ZONES.put(ZoneType.Sideboard, "sideboard");
|
ZONES.put(ZoneType.Sideboard, "sideboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int humanLife = -1;
|
static class PlayerState {
|
||||||
private int computerLife = -1;
|
private int life = -1;
|
||||||
private String humanCounters = "";
|
private String counters = "";
|
||||||
private String computerCounters = "";
|
private String manaPool = "";
|
||||||
private String humanManaPool = "";
|
private String persistentMana = "";
|
||||||
private String computerManaPool = "";
|
private int landsPlayed = 0;
|
||||||
private String humanPersistentMana = "";
|
private int landsPlayedLastTurn = 0;
|
||||||
private String computerPersistentMana = "";
|
private String precast = null;
|
||||||
private int humanLandsPlayed = 0;
|
private String putOnStack = null;
|
||||||
private int computerLandsPlayed = 0;
|
private final Map<ZoneType, String> cardTexts = new EnumMap<>(ZoneType.class);
|
||||||
private int humanLandsPlayedLastTurn = 0;
|
}
|
||||||
private int computerLandsPlayedLastTurn = 0;
|
private final List<PlayerState> playerStates = new ArrayList<>();
|
||||||
|
|
||||||
private boolean puzzleCreatorState = false;
|
private boolean puzzleCreatorState = false;
|
||||||
|
|
||||||
private final Map<ZoneType, String> humanCardTexts = new EnumMap<>(ZoneType.class);
|
|
||||||
private final Map<ZoneType, String> aiCardTexts = new EnumMap<>(ZoneType.class);
|
|
||||||
|
|
||||||
private final Map<Integer, Card> idToCard = new HashMap<>();
|
private final Map<Integer, Card> idToCard = new HashMap<>();
|
||||||
private final Map<Card, Integer> cardToAttachId = new HashMap<>();
|
private final Map<Card, Integer> cardToAttachId = new HashMap<>();
|
||||||
private final Map<Card, Integer> cardToEnchantPlayerId = new HashMap<>();
|
private final Map<Card, Player> cardToEnchantPlayerId = new HashMap<>();
|
||||||
private final Map<Card, Integer> markedDamage = new HashMap<>();
|
private final Map<Card, Integer> markedDamage = new HashMap<>();
|
||||||
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
|
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
|
||||||
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
|
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
|
||||||
@@ -98,12 +96,6 @@ public abstract class GameState {
|
|||||||
|
|
||||||
private String tAdvancePhase = "NONE";
|
private String tAdvancePhase = "NONE";
|
||||||
|
|
||||||
private String precastHuman = null;
|
|
||||||
private String precastAI = null;
|
|
||||||
|
|
||||||
private String putOnStackHuman = null;
|
|
||||||
private String putOnStackAI = null;
|
|
||||||
|
|
||||||
private int turn = 1;
|
private int turn = 1;
|
||||||
|
|
||||||
private boolean removeSummoningSickness = false;
|
private boolean removeSummoningSickness = false;
|
||||||
@@ -134,32 +126,27 @@ public abstract class GameState {
|
|||||||
sb.append("[state]\n");
|
sb.append("[state]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(TextUtil.concatNoSpace("humanlife=", String.valueOf(humanLife), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("ailife=", String.valueOf(computerLife), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("humanlandsplayed=", String.valueOf(humanLandsPlayed), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("ailandsplayed=", String.valueOf(computerLandsPlayed), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("humanlandsplayedlastturn=", String.valueOf(humanLandsPlayedLastTurn), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("ailandsplayedlastturn=", String.valueOf(computerLandsPlayedLastTurn), "\n"));
|
|
||||||
sb.append(TextUtil.concatNoSpace("turn=", String.valueOf(turn), "\n"));
|
sb.append(TextUtil.concatNoSpace("turn=", String.valueOf(turn), "\n"));
|
||||||
|
|
||||||
if (!humanCounters.isEmpty()) {
|
|
||||||
sb.append(TextUtil.concatNoSpace("humancounters=", humanCounters, "\n"));
|
|
||||||
}
|
|
||||||
if (!computerCounters.isEmpty()) {
|
|
||||||
sb.append(TextUtil.concatNoSpace("aicounters=", computerCounters, "\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!humanManaPool.isEmpty()) {
|
|
||||||
sb.append(TextUtil.concatNoSpace("humanmanapool=", humanManaPool, "\n"));
|
|
||||||
}
|
|
||||||
if (!computerManaPool.isEmpty()) {
|
|
||||||
sb.append(TextUtil.concatNoSpace("aimanapool=", computerManaPool, "\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
||||||
sb.append(TextUtil.concatNoSpace("activephase=", tChangePhase, "\n"));
|
sb.append(TextUtil.concatNoSpace("activephase=", tChangePhase, "\n"));
|
||||||
appendCards(humanCardTexts, "human", sb);
|
|
||||||
appendCards(aiCardTexts, "ai", sb);
|
int playerIndex = 0;
|
||||||
|
for (PlayerState p : playerStates) {
|
||||||
|
String prefix = "p" + playerIndex++;
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "life=", String.valueOf(p.life), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "landsplayed=", String.valueOf(p.landsPlayed), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "landsplayedlastturn=", String.valueOf(p.landsPlayedLastTurn), "\n"));
|
||||||
|
if (!p.counters.isEmpty()) {
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "counters=", p.counters, "\n"));
|
||||||
|
}
|
||||||
|
if (!p.manaPool.isEmpty()) {
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "manapool=", p.manaPool, "\n"));
|
||||||
|
}
|
||||||
|
if (!p.persistentMana.isEmpty()) {
|
||||||
|
sb.append(TextUtil.concatNoSpace(prefix + "persistentmana=", p.persistentMana, "\n"));
|
||||||
|
}
|
||||||
|
appendCards(p.cardTexts, prefix, sb);
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,33 +156,21 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initFromGame(Game game) throws Exception {
|
public void initFromGame(Game game) {
|
||||||
FCollectionView<Player> players = game.getPlayers();
|
playerStates.clear();
|
||||||
// Can only serialize a two player game with one AI and one human.
|
for (Player player : game.getPlayers()) {
|
||||||
if (players.size() != 2) {
|
PlayerState p = new PlayerState();
|
||||||
throw new Exception("Game not supported");
|
p.life = player.getLife();
|
||||||
|
p.landsPlayed = player.getLandsPlayedThisTurn();
|
||||||
|
p.landsPlayedLastTurn = player.getLandsPlayedLastTurn();
|
||||||
|
p.counters = countersToString(player.getCounters());
|
||||||
|
p.manaPool = processManaPool(player.getManaPool());
|
||||||
|
playerStates.add(p);
|
||||||
}
|
}
|
||||||
final Player human = game.getPlayers().get(0);
|
|
||||||
final Player ai = game.getPlayers().get(1);
|
|
||||||
if (!human.getController().isGuiPlayer() || !ai.getController().isAI()) {
|
|
||||||
throw new Exception("Game not supported");
|
|
||||||
}
|
|
||||||
humanLife = human.getLife();
|
|
||||||
computerLife = ai.getLife();
|
|
||||||
humanLandsPlayed = human.getLandsPlayedThisTurn();
|
|
||||||
computerLandsPlayed = ai.getLandsPlayedThisTurn();
|
|
||||||
humanLandsPlayedLastTurn = human.getLandsPlayedLastTurn();
|
|
||||||
computerLandsPlayedLastTurn = ai.getLandsPlayedLastTurn();
|
|
||||||
humanCounters = countersToString(human.getCounters());
|
|
||||||
computerCounters = countersToString(ai.getCounters());
|
|
||||||
humanManaPool = processManaPool(human.getManaPool());
|
|
||||||
computerManaPool = processManaPool(ai.getManaPool());
|
|
||||||
|
|
||||||
tChangePlayer = game.getPhaseHandler().getPlayerTurn() == ai ? "ai" : "human";
|
tChangePlayer = "p" + game.getPlayers().indexOf(game.getPhaseHandler().getPlayerTurn());
|
||||||
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
||||||
turn = game.getPhaseHandler().getTurn();
|
turn = game.getPhaseHandler().getTurn();
|
||||||
aiCardTexts.clear();
|
|
||||||
humanCardTexts.clear();
|
|
||||||
|
|
||||||
// Mark the cards that need their ID remembered for various reasons
|
// Mark the cards that need their ID remembered for various reasons
|
||||||
cardsReferencedByID.clear();
|
cardsReferencedByID.clear();
|
||||||
@@ -238,8 +213,9 @@ public abstract class GameState {
|
|||||||
for (ZoneType zone : ZONES.keySet()) {
|
for (ZoneType zone : ZONES.keySet()) {
|
||||||
// Init texts to empty, so that restoring will clear the state
|
// Init texts to empty, so that restoring will clear the state
|
||||||
// if the zone had no cards in it (e.g. empty hand).
|
// if the zone had no cards in it (e.g. empty hand).
|
||||||
aiCardTexts.put(zone, "");
|
for (PlayerState p : playerStates) {
|
||||||
humanCardTexts.put(zone, "");
|
p.cardTexts.put(zone, "");
|
||||||
|
}
|
||||||
for (Card card : game.getCardsIncludePhasingIn(zone)) {
|
for (Card card : game.getCardsIncludePhasingIn(zone)) {
|
||||||
if (card.getName().equals("Puzzle Goal") && card.getOracleText().contains("New Puzzle")) {
|
if (card.getName().equals("Puzzle Goal") && card.getOracleText().contains("New Puzzle")) {
|
||||||
puzzleCreatorState = true;
|
puzzleCreatorState = true;
|
||||||
@@ -247,18 +223,35 @@ public abstract class GameState {
|
|||||||
if (card instanceof DetachedCardEffect) {
|
if (card instanceof DetachedCardEffect) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
addCard(zone, card.getController() == ai ? aiCardTexts : humanCardTexts, card);
|
int playerIndex = game.getPlayers().indexOf(card.getController());
|
||||||
|
addCard(zone, playerStates.get(playerIndex).cardTexts, card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPlayerString(Player p) {
|
||||||
|
return "P" + p.getGame().getPlayers().indexOf(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Player parsePlayerString(Game game, String str) {
|
||||||
|
if (str.equalsIgnoreCase("HUMAN")) {
|
||||||
|
return game.getPlayers().get(0);
|
||||||
|
} else if (str.equalsIgnoreCase("AI")) {
|
||||||
|
return game.getPlayers().get(1);
|
||||||
|
} else if (str.startsWith("P") && Character.isDigit(str.charAt(1))) {
|
||||||
|
return game.getPlayers().get(Integer.parseInt(String.valueOf(str.charAt(1))));
|
||||||
|
} else {
|
||||||
|
return game.getPlayers().get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addCard(ZoneType zoneType, Map<ZoneType, String> cardTexts, Card c) {
|
private void addCard(ZoneType zoneType, Map<ZoneType, String> cardTexts, Card c) {
|
||||||
StringBuilder newText = new StringBuilder(cardTexts.get(zoneType));
|
StringBuilder newText = new StringBuilder(cardTexts.get(zoneType));
|
||||||
if (newText.length() > 0) {
|
if (newText.length() > 0) {
|
||||||
newText.append(";");
|
newText.append(";");
|
||||||
}
|
}
|
||||||
if (c.isToken()) {
|
if (c.isToken()) {
|
||||||
newText.append("t:").append(new TokenInfo(c).toString());
|
newText.append("t:").append(new TokenInfo(c));
|
||||||
} else {
|
} else {
|
||||||
if (c.getPaperCard() == null) {
|
if (c.getPaperCard() == null) {
|
||||||
return;
|
return;
|
||||||
@@ -281,8 +274,7 @@ public abstract class GameState {
|
|||||||
|
|
||||||
if (zoneType == ZoneType.Battlefield) {
|
if (zoneType == ZoneType.Battlefield) {
|
||||||
if (c.getOwner() != c.getController()) {
|
if (c.getOwner() != c.getController()) {
|
||||||
// TODO: Handle more than 2-player games.
|
newText.append("|Owner:").append(getPlayerString(c.getOwner()));
|
||||||
newText.append("|Owner:" + (c.getOwner().isAI() ? "AI" : "Human"));
|
|
||||||
}
|
}
|
||||||
if (c.isTapped()) {
|
if (c.isTapped()) {
|
||||||
newText.append("|Tapped");
|
newText.append("|Tapped");
|
||||||
@@ -298,7 +290,7 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
if (c.isPhasedOut()) {
|
if (c.isPhasedOut()) {
|
||||||
newText.append("|PhasedOut:");
|
newText.append("|PhasedOut:");
|
||||||
newText.append(c.getPhasedOut().isAI() ? "AI" : "HUMAN");
|
newText.append(getPlayerString(c.getPhasedOut()));
|
||||||
}
|
}
|
||||||
if (c.isFaceDown()) {
|
if (c.isFaceDown()) {
|
||||||
newText.append("|FaceDown");
|
newText.append("|FaceDown");
|
||||||
@@ -319,10 +311,8 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (c.getPlayerAttachedTo() != null) {
|
if (c.getPlayerAttachedTo() != null) {
|
||||||
// TODO: improve this for game states with more than two players
|
|
||||||
newText.append("|EnchantingPlayer:");
|
newText.append("|EnchantingPlayer:");
|
||||||
Player p = c.getPlayerAttachedTo();
|
newText.append(getPlayerString(c.getPlayerAttachedTo()));
|
||||||
newText.append(p.getController().isAI() ? "AI" : "HUMAN");
|
|
||||||
} else if (c.isAttachedToEntity()) {
|
} else if (c.isAttachedToEntity()) {
|
||||||
newText.append("|AttachedTo:").append(c.getEntityAttachedTo().getId());
|
newText.append("|AttachedTo:").append(c.getEntityAttachedTo().getId());
|
||||||
}
|
}
|
||||||
@@ -348,11 +338,8 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> chosenCardIds = Lists.newArrayList();
|
List<String> chosenCardIds = Lists.newArrayList();
|
||||||
for (Object obj : c.getChosenCards()) {
|
for (Card obj : c.getChosenCards()) {
|
||||||
if (obj instanceof Card) {
|
chosenCardIds.add(String.valueOf(obj.getId()));
|
||||||
int id = ((Card)obj).getId();
|
|
||||||
chosenCardIds.add(String.valueOf(id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!chosenCardIds.isEmpty()) {
|
if (!chosenCardIds.isEmpty()) {
|
||||||
newText.append("|ChosenCards:").append(TextUtil.join(chosenCardIds, ","));
|
newText.append("|ChosenCards:").append(TextUtil.join(chosenCardIds, ","));
|
||||||
@@ -465,16 +452,36 @@ public abstract class GameState {
|
|||||||
|
|
||||||
public void parse(InputStream in) throws Exception {
|
public void parse(InputStream in) throws Exception {
|
||||||
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
||||||
|
parse(br.lines());
|
||||||
String line;
|
|
||||||
while ((line = br.readLine()) != null) {
|
|
||||||
parseLine(line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parse(List<String> lines) {
|
public void parse(List<String> lines) {
|
||||||
for (String line : lines) {
|
parse(lines.stream());
|
||||||
parseLine(line);
|
}
|
||||||
|
|
||||||
|
public void parse(Stream<String> lines) {
|
||||||
|
playerStates.clear();
|
||||||
|
lines.forEach(this::parseLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private PlayerState getPlayerState(int index) {
|
||||||
|
while (index >= playerStates.size()) {
|
||||||
|
playerStates.add(new PlayerState());
|
||||||
|
}
|
||||||
|
return playerStates.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerState getPlayerState(String key) {
|
||||||
|
if (key.startsWith("human")) {
|
||||||
|
return getPlayerState(0);
|
||||||
|
} else if (key.startsWith("ai")) {
|
||||||
|
return getPlayerState(1);
|
||||||
|
} else if (key.startsWith("p") && Character.isDigit(key.charAt(1))) {
|
||||||
|
return getPlayerState(Integer.parseInt(String.valueOf(key.charAt(1))));
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown player state key: " + key);
|
||||||
|
return new PlayerState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,142 +502,56 @@ public abstract class GameState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isHuman = categoryName.startsWith("human");
|
|
||||||
|
|
||||||
if (categoryName.equals("turn")) {
|
if (categoryName.equals("turn")) {
|
||||||
turn = Integer.parseInt(categoryValue);
|
turn = Integer.parseInt(categoryValue);
|
||||||
}
|
} else if (categoryName.equals("removesummoningsickness")) {
|
||||||
|
|
||||||
else if (categoryName.equals("removesummoningsickness")) {
|
|
||||||
removeSummoningSickness = categoryValue.equalsIgnoreCase("true");
|
removeSummoningSickness = categoryValue.equalsIgnoreCase("true");
|
||||||
}
|
} else if (categoryName.endsWith("life")) {
|
||||||
|
getPlayerState(categoryName).life = Integer.parseInt(categoryValue);
|
||||||
else if (categoryName.endsWith("life")) {
|
} else if (categoryName.endsWith("counters")) {
|
||||||
if (isHuman)
|
getPlayerState(categoryName).counters = categoryValue;
|
||||||
humanLife = Integer.parseInt(categoryValue);
|
} else if (categoryName.endsWith("landsplayed")) {
|
||||||
else
|
getPlayerState(categoryName).landsPlayed = Integer.parseInt(categoryValue);
|
||||||
computerLife = Integer.parseInt(categoryValue);
|
} else if (categoryName.endsWith("landsplayedlastturn")) {
|
||||||
}
|
getPlayerState(categoryName).landsPlayedLastTurn = Integer.parseInt(categoryValue);
|
||||||
|
} else if (categoryName.endsWith("play") || categoryName.endsWith("battlefield")) {
|
||||||
else if (categoryName.endsWith("counters")) {
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Battlefield, categoryValue);
|
||||||
if (isHuman)
|
} else if (categoryName.endsWith("hand")) {
|
||||||
humanCounters = categoryValue;
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Hand, categoryValue);
|
||||||
else
|
} else if (categoryName.endsWith("graveyard")) {
|
||||||
computerCounters = categoryValue;
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Graveyard, categoryValue);
|
||||||
}
|
} else if (categoryName.endsWith("library")) {
|
||||||
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Library, categoryValue);
|
||||||
else if (categoryName.endsWith("landsplayed")) {
|
} else if (categoryName.endsWith("exile")) {
|
||||||
if (isHuman)
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Exile, categoryValue);
|
||||||
humanLandsPlayed = Integer.parseInt(categoryValue);
|
} else if (categoryName.endsWith("command")) {
|
||||||
else
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Command, categoryValue);
|
||||||
computerLandsPlayed = Integer.parseInt(categoryValue);
|
} else if (categoryName.endsWith("sideboard")) {
|
||||||
}
|
getPlayerState(categoryName).cardTexts.put(ZoneType.Sideboard, categoryValue);
|
||||||
|
} else if (categoryName.startsWith("ability")) {
|
||||||
else if (categoryName.endsWith("landsplayedlastturn")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanLandsPlayedLastTurn = Integer.parseInt(categoryValue);
|
|
||||||
else
|
|
||||||
computerLandsPlayedLastTurn = Integer.parseInt(categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("play") || categoryName.endsWith("battlefield")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Battlefield, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Battlefield, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("hand")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Hand, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Hand, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("graveyard")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Graveyard, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Graveyard, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("library")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Library, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Library, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("exile")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Exile, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Exile, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("command")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Command, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Command, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("sideboard")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanCardTexts.put(ZoneType.Sideboard, categoryValue);
|
|
||||||
else
|
|
||||||
aiCardTexts.put(ZoneType.Sideboard, categoryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.startsWith("ability")) {
|
|
||||||
abilityString.put(categoryName.substring("ability".length()), categoryValue);
|
abilityString.put(categoryName.substring("ability".length()), categoryValue);
|
||||||
}
|
} else if (categoryName.endsWith("precast")) {
|
||||||
|
getPlayerState(categoryName).precast = categoryValue;
|
||||||
else if (categoryName.endsWith("precast")) {
|
} else if (categoryName.endsWith("putonstack")) {
|
||||||
if (isHuman)
|
getPlayerState(categoryName).putOnStack = categoryValue;
|
||||||
precastHuman = categoryValue;
|
} else if (categoryName.endsWith("manapool")) {
|
||||||
else
|
getPlayerState(categoryName).manaPool = categoryValue;
|
||||||
precastAI = categoryValue;
|
} else if (categoryName.endsWith("persistentmana")) {
|
||||||
}
|
getPlayerState(categoryName).persistentMana = categoryValue;
|
||||||
|
} else {
|
||||||
else if (categoryName.endsWith("putonstack")) {
|
System.err.println("Unknown key: " + categoryName);
|
||||||
if (isHuman)
|
|
||||||
putOnStackHuman = categoryValue;
|
|
||||||
else
|
|
||||||
putOnStackAI = categoryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("manapool")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanManaPool = categoryValue;
|
|
||||||
else
|
|
||||||
computerManaPool = categoryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (categoryName.endsWith("persistentmana")) {
|
|
||||||
if (isHuman)
|
|
||||||
humanPersistentMana = categoryValue;
|
|
||||||
else
|
|
||||||
computerPersistentMana = categoryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
System.out.println("Unknown key: " + categoryName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyToGame(final Game game) {
|
public void applyToGame(final Game game) {
|
||||||
game.getAction().invoke(new Runnable() {
|
game.getAction().invoke(() -> applyGameOnThread(game));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
applyGameOnThread(game);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void applyGameOnThread(final Game game) {
|
protected void applyGameOnThread(final Game game) {
|
||||||
final Player human = game.getPlayers().get(0);
|
if (game.getPlayers().size() != playerStates.size()) {
|
||||||
final Player ai = game.getPlayers().get(1);
|
throw new RuntimeException("Non-matching number of players, (" +
|
||||||
|
game.getPlayers().size() + " vs. " + playerStates.size() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
idToCard.clear();
|
idToCard.clear();
|
||||||
cardToAttachId.clear();
|
cardToAttachId.clear();
|
||||||
@@ -647,32 +568,21 @@ public abstract class GameState {
|
|||||||
cardToScript.clear();
|
cardToScript.clear();
|
||||||
cardAttackMap.clear();
|
cardAttackMap.clear();
|
||||||
|
|
||||||
Player newPlayerTurn = tChangePlayer.equalsIgnoreCase("human") ? human : tChangePlayer.equalsIgnoreCase("ai") ? ai : null;
|
int playerTurn = playerStates.indexOf(getPlayerState(tChangePlayer));
|
||||||
|
Player newPlayerTurn = game.getPlayers().get(playerTurn);
|
||||||
PhaseType newPhase = tChangePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
PhaseType newPhase = tChangePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
||||||
PhaseType advPhase = tAdvancePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tAdvancePhase);
|
PhaseType advPhase = tAdvancePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tAdvancePhase);
|
||||||
|
|
||||||
// Set stack to resolving so things won't trigger/effects be checked right away
|
// Set stack to resolving so things won't trigger/effects be checked right away
|
||||||
game.getStack().setResolving(true);
|
game.getStack().setResolving(true);
|
||||||
|
|
||||||
updateManaPool(human, humanManaPool, true, false);
|
|
||||||
updateManaPool(ai, computerManaPool, true, false);
|
|
||||||
updateManaPool(human, humanPersistentMana, false, true);
|
|
||||||
updateManaPool(ai, computerPersistentMana, false, true);
|
|
||||||
|
|
||||||
if (!humanCounters.isEmpty()) {
|
|
||||||
applyCountersToGameEntity(human, humanCounters);
|
|
||||||
}
|
|
||||||
if (!computerCounters.isEmpty()) {
|
|
||||||
applyCountersToGameEntity(ai, computerCounters);
|
|
||||||
}
|
|
||||||
|
|
||||||
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn, turn);
|
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn, turn);
|
||||||
|
|
||||||
game.getTriggerHandler().setSuppressAllTriggers(true);
|
game.getTriggerHandler().setSuppressAllTriggers(true);
|
||||||
|
|
||||||
setupPlayerState(humanLife, humanCardTexts, human, humanLandsPlayed, humanLandsPlayedLastTurn);
|
for (int i = 0; i < playerStates.size(); i++) {
|
||||||
setupPlayerState(computerLife, aiCardTexts, ai, computerLandsPlayed, computerLandsPlayedLastTurn);
|
setupPlayerState(game.getPlayers().get(i), playerStates.get(i));
|
||||||
|
}
|
||||||
handleCardAttachments();
|
handleCardAttachments();
|
||||||
handleChosenEntities();
|
handleChosenEntities();
|
||||||
handleRememberedEntities();
|
handleRememberedEntities();
|
||||||
@@ -712,10 +622,11 @@ public abstract class GameState {
|
|||||||
|
|
||||||
// Set negative or zero life after state effects if need be, important for some puzzles that rely on
|
// Set negative or zero life after state effects if need be, important for some puzzles that rely on
|
||||||
// pre-setting negative life (e.g. PS_NEO4).
|
// pre-setting negative life (e.g. PS_NEO4).
|
||||||
if (humanLife <= 0) {
|
for (int i = 0; i < playerStates.size(); i++) {
|
||||||
human.setLife(humanLife, null);
|
int life = playerStates.get(i).life;
|
||||||
} else if (computerLife <= 0) {
|
if (life <= 0) {
|
||||||
ai.setLife(computerLife, null);
|
game.getPlayers().get(i).setLife(life, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,12 +657,7 @@ public abstract class GameState {
|
|||||||
produced.put("PersistentMana", "True");
|
produced.put("PersistentMana", "True");
|
||||||
}
|
}
|
||||||
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
|
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
|
||||||
game.getAction().invoke(new Runnable() {
|
game.getAction().invoke(() -> abMana.produceMana(null));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
abMana.produceMana(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,8 +738,7 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int parseTargetInScript(final String tgtDef) {
|
private int parseTargetInScript(final String tgtDef) {
|
||||||
int tgtID = TARGET_NONE;
|
int tgtID;
|
||||||
|
|
||||||
if (tgtDef.equalsIgnoreCase("human")) {
|
if (tgtDef.equalsIgnoreCase("human")) {
|
||||||
tgtID = TARGET_HUMAN;
|
tgtID = TARGET_HUMAN;
|
||||||
} else if (tgtDef.equalsIgnoreCase("ai")) {
|
} else if (tgtDef.equalsIgnoreCase("ai")) {
|
||||||
@@ -972,37 +877,23 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handlePrecastSpells(final Game game) {
|
private void handlePrecastSpells(final Game game) {
|
||||||
Player human = game.getPlayers().get(0);
|
for (int i = 0; i < playerStates.size(); i++) {
|
||||||
Player ai = game.getPlayers().get(1);
|
if (playerStates.get(i).precast != null) {
|
||||||
|
String[] spellList = TextUtil.split(playerStates.get(i).precast, ';');
|
||||||
if (precastHuman != null) {
|
for (String spell : spellList) {
|
||||||
String[] spellList = TextUtil.split(precastHuman, ';');
|
precastSpellFromCard(spell, game.getPlayers().get(i), game);
|
||||||
for (String spell : spellList) {
|
}
|
||||||
precastSpellFromCard(spell, human, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (precastAI != null) {
|
|
||||||
String[] spellList = TextUtil.split(precastAI, ';');
|
|
||||||
for (String spell : spellList) {
|
|
||||||
precastSpellFromCard(spell, ai, game);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddSAsToStack(final Game game) {
|
private void handleAddSAsToStack(final Game game) {
|
||||||
Player human = game.getPlayers().get(0);
|
for (int i = 0; i < playerStates.size(); i++) {
|
||||||
Player ai = game.getPlayers().get(1);
|
if (playerStates.get(i).putOnStack != null) {
|
||||||
|
String[] spellList = TextUtil.split(playerStates.get(i).putOnStack, ';');
|
||||||
if (putOnStackHuman != null) {
|
for (String spell : spellList) {
|
||||||
String[] spellList = TextUtil.split(putOnStackHuman, ';');
|
precastSpellFromCard(spell, game.getPlayers().get(i), game, true);
|
||||||
for (String spell : spellList) {
|
}
|
||||||
precastSpellFromCard(spell, human, game, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (putOnStackAI != null) {
|
|
||||||
String[] spellList = TextUtil.split(putOnStackAI, ';');
|
|
||||||
for (String spell : spellList) {
|
|
||||||
precastSpellFromCard(spell, ai, game, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1138,14 +1029,9 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enchant players by ID
|
// Enchant players
|
||||||
for (Entry<Card, Integer> entry : cardToEnchantPlayerId.entrySet()) {
|
for (Entry<Card, Player> entry : cardToEnchantPlayerId.entrySet()) {
|
||||||
// TODO: improve this for game states with more than two players
|
entry.getKey().attachToEntity(entry.getValue(), null);
|
||||||
Card attacher = entry.getKey();
|
|
||||||
Game game = attacher.getGame();
|
|
||||||
Player attachedTo = entry.getValue() == TARGET_AI ? game.getPlayers().get(1) : game.getPlayers().get(0);
|
|
||||||
|
|
||||||
attacher.attachToEntity(attachedTo, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1184,7 +1070,7 @@ public abstract class GameState {
|
|||||||
top.removeCloneState(top.getMutatedTimestamp());
|
top.removeCloneState(top.getMutatedTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
final Long ts = game.getNextTimestamp();
|
final long ts = game.getNextTimestamp();
|
||||||
top.setMutatedTimestamp(ts);
|
top.setMutatedTimestamp(ts);
|
||||||
if (top.getCurrentStateName() != CardStateName.FaceDown) {
|
if (top.getCurrentStateName() != CardStateName.FaceDown) {
|
||||||
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(top, null/*FIXME*/);
|
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(top, null/*FIXME*/);
|
||||||
@@ -1207,7 +1093,7 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p, final int landsPlayed, final int landsPlayedLastTurn) {
|
private void setupPlayerState(final Player p, final PlayerState state) {
|
||||||
// Lock check static as we setup player state
|
// Lock check static as we setup player state
|
||||||
|
|
||||||
// Clear all zones first, this ensures that any lingering cards and effects (e.g. in command zone) get cleared up
|
// Clear all zones first, this ensures that any lingering cards and effects (e.g. in command zone) get cleared up
|
||||||
@@ -1219,14 +1105,14 @@ public abstract class GameState {
|
|||||||
p.setCommanders(Lists.newArrayList());
|
p.setCommanders(Lists.newArrayList());
|
||||||
|
|
||||||
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<>(ZoneType.class);
|
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<>(ZoneType.class);
|
||||||
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
|
for (Entry<ZoneType, String> kv : state.cardTexts.entrySet()) {
|
||||||
String value = kv.getValue();
|
String value = kv.getValue();
|
||||||
playerCards.put(kv.getKey(), processCardsForZone(value.isEmpty() ? new String[0] : value.split(";"), p));
|
playerCards.put(kv.getKey(), processCardsForZone(value.isEmpty() ? new String[0] : value.split(";"), p));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (life >= 0) p.setLife(life, null);
|
if (state.life >= 0) p.setLife(state.life, null);
|
||||||
p.setLandsPlayedThisTurn(landsPlayed);
|
p.setLandsPlayedThisTurn(state.landsPlayed);
|
||||||
p.setLandsPlayedLastTurn(landsPlayedLastTurn);
|
p.setLandsPlayedLastTurn(state.landsPlayedLastTurn);
|
||||||
|
|
||||||
p.clearPaidForSA();
|
p.clearPaidForSA();
|
||||||
|
|
||||||
@@ -1275,6 +1161,13 @@ public abstract class GameState {
|
|||||||
for (Card cmd : p.getCommanders()) {
|
for (Card cmd : p.getCommanders()) {
|
||||||
p.getZone(ZoneType.Command).add(Player.createCommanderEffect(p.getGame(), cmd));
|
p.getZone(ZoneType.Command).add(Player.createCommanderEffect(p.getGame(), cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateManaPool(p, state.manaPool, true, false);
|
||||||
|
updateManaPool(p, state.persistentMana, false, true);
|
||||||
|
|
||||||
|
if (!state.counters.isEmpty()) {
|
||||||
|
applyCountersToGameEntity(p, state.counters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1330,9 +1223,7 @@ public abstract class GameState {
|
|||||||
c.setMonstrous(true);
|
c.setMonstrous(true);
|
||||||
} else if (info.startsWith("PhasedOut")) {
|
} else if (info.startsWith("PhasedOut")) {
|
||||||
String tgt = info.substring(info.indexOf(':') + 1);
|
String tgt = info.substring(info.indexOf(':') + 1);
|
||||||
Player human = player.getGame().getPlayers().get(0);
|
c.setPhasedOut(parsePlayerString(player.getGame(), tgt));
|
||||||
Player ai = player.getGame().getPlayers().get(1);
|
|
||||||
c.setPhasedOut(tgt.equalsIgnoreCase("AI") ? ai : human);
|
|
||||||
} else if (info.startsWith("Counters:")) {
|
} else if (info.startsWith("Counters:")) {
|
||||||
applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1));
|
applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1));
|
||||||
} else if (info.startsWith("SummonSick")) {
|
} else if (info.startsWith("SummonSick")) {
|
||||||
@@ -1377,16 +1268,12 @@ public abstract class GameState {
|
|||||||
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
|
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
|
||||||
cardToAttachId.put(c, id);
|
cardToAttachId.put(c, id);
|
||||||
} else if (info.startsWith("EnchantingPlayer:")) {
|
} else if (info.startsWith("EnchantingPlayer:")) {
|
||||||
// TODO: improve this for game states with more than two players
|
|
||||||
String tgt = info.substring(info.indexOf(':') + 1);
|
String tgt = info.substring(info.indexOf(':') + 1);
|
||||||
cardToEnchantPlayerId.put(c, tgt.equalsIgnoreCase("AI") ? TARGET_AI : TARGET_HUMAN);
|
cardToEnchantPlayerId.put(c, parsePlayerString(player.getGame(), tgt));
|
||||||
} else if (info.startsWith("Owner:")) {
|
} else if (info.startsWith("Owner:")) {
|
||||||
// TODO: improve this for game states with more than two players
|
|
||||||
Player human = player.getGame().getPlayers().get(0);
|
|
||||||
Player ai = player.getGame().getPlayers().get(1);
|
|
||||||
String owner = info.substring(info.indexOf(':') + 1);
|
String owner = info.substring(info.indexOf(':') + 1);
|
||||||
Player controller = c.getController();
|
Player controller = c.getController();
|
||||||
c.setOwner(owner.equalsIgnoreCase("AI") ? ai : human);
|
c.setOwner(parsePlayerString(player.getGame(), owner));
|
||||||
c.setController(controller, c.getGame().getNextTimestamp());
|
c.setController(controller, c.getGame().getNextTimestamp());
|
||||||
} else if (info.startsWith("Ability:")) {
|
} else if (info.startsWith("Ability:")) {
|
||||||
String abString = info.substring(info.indexOf(':') + 1).toLowerCase();
|
String abString = info.substring(info.indexOf(':') + 1).toLowerCase();
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("ChainVeil")) {
|
} else if (logic.equals("ChainVeil")) {
|
||||||
if (!phase.isPlayerTurn(ai) || !phase.getPhase().equals(PhaseType.MAIN2)
|
if (!phase.isPlayerTurn(ai) || !phase.getPhase().equals(PhaseType.MAIN2) || ai.getPlaneswalkersInPlay().isEmpty()) {
|
||||||
|| CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "Planeswalker").isEmpty()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
|||||||
ManaPool mp = ai.getManaPool();
|
ManaPool mp = ai.getManaPool();
|
||||||
Mana test = null;
|
Mana test = null;
|
||||||
if (mp.isEmpty()) {
|
if (mp.isEmpty()) {
|
||||||
|
// TODO use color from ability
|
||||||
test = new Mana((byte) ManaAtom.COLORLESS, source, null);
|
test = new Mana((byte) ManaAtom.COLORLESS, source, null);
|
||||||
mp.addMana(test, false);
|
mp.addMana(test, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
repParams.put(AbilityKey.Origin, card.getZone().getZoneType());
|
repParams.put(AbilityKey.Origin, card.getZone().getZoneType());
|
||||||
repParams.put(AbilityKey.Destination, ZoneType.Battlefield);
|
repParams.put(AbilityKey.Destination, ZoneType.Battlefield);
|
||||||
repParams.put(AbilityKey.Source, sa.getHostCard());
|
repParams.put(AbilityKey.Source, sa.getHostCard());
|
||||||
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.Other);
|
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.CantHappen);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) {
|
if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) {
|
||||||
if (ComputerUtilAbility.isFullyTargetable(sa)) { // Volcanic Offering: only prompt if second part can happen too
|
if (!ComputerUtilAbility.isFullyTargetable(sa)) { // Volcanic Offering: only prompt if second part can happen too
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0);
|
Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0);
|
||||||
|
|||||||
@@ -76,21 +76,23 @@ public class GameCopier {
|
|||||||
for (RegisteredPlayer p : origPlayers) {
|
for (RegisteredPlayer p : origPlayers) {
|
||||||
newPlayers.add(clonePlayer(p));
|
newPlayers.add(clonePlayer(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
GameRules currentRules = origGame.getRules();
|
GameRules currentRules = origGame.getRules();
|
||||||
Match newMatch = new Match(currentRules, newPlayers, origGame.getView().getTitle());
|
Match newMatch = new Match(currentRules, newPlayers, origGame.getView().getTitle());
|
||||||
Game newGame = new Game(newPlayers, currentRules, newMatch);
|
Game newGame = new Game(newPlayers, currentRules, newMatch);
|
||||||
|
|
||||||
for (int i = 0; i < origGame.getPlayers().size(); i++) {
|
for (int i = 0; i < origGame.getPlayers().size(); i++) {
|
||||||
Player origPlayer = origGame.getPlayers().get(i);
|
Player origPlayer = origGame.getPlayers().get(i);
|
||||||
Player newPlayer = newGame.getPlayers().get(i);
|
Player newPlayer = newGame.getPlayers().get(i);
|
||||||
newPlayer.setLife(origPlayer.getLife(), null);
|
newPlayer.setLife(origPlayer.getLife(), null);
|
||||||
newPlayer.setActivateLoyaltyAbilityThisTurn(origPlayer.getActivateLoyaltyAbilityThisTurn());
|
|
||||||
for (int j = 0; j < origPlayer.getSpellsCastThisTurn(); j++)
|
|
||||||
newPlayer.addSpellCastThisTurn();
|
|
||||||
newPlayer.setLandsPlayedThisTurn(origPlayer.getLandsPlayedThisTurn());
|
|
||||||
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
|
|
||||||
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
||||||
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
||||||
newPlayer.setLifeGainedThisTurn(origPlayer.getLifeGainedThisTurn());
|
newPlayer.setLifeGainedThisTurn(origPlayer.getLifeGainedThisTurn());
|
||||||
|
newPlayer.setLifeStartedThisTurnWith(origPlayer.getLifeStartedThisTurnWith());
|
||||||
|
newPlayer.setDamageReceivedThisTurn(origPlayer.getDamageReceivedThisTurn());
|
||||||
|
newPlayer.setActivateLoyaltyAbilityThisTurn(origPlayer.getActivateLoyaltyAbilityThisTurn());
|
||||||
|
newPlayer.setLandsPlayedThisTurn(origPlayer.getLandsPlayedThisTurn());
|
||||||
|
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
|
||||||
newPlayer.setBlessing(origPlayer.hasBlessing());
|
newPlayer.setBlessing(origPlayer.hasBlessing());
|
||||||
newPlayer.setRevolt(origPlayer.hasRevolt());
|
newPlayer.setRevolt(origPlayer.hasRevolt());
|
||||||
newPlayer.setLibrarySearched(origPlayer.getLibrarySearched());
|
newPlayer.setLibrarySearched(origPlayer.getLibrarySearched());
|
||||||
@@ -350,6 +352,7 @@ public class GameCopier {
|
|||||||
newCard.setPTBoost(c.getPTBoostTable());
|
newCard.setPTBoost(c.getPTBoostTable());
|
||||||
// TODO copy by map
|
// TODO copy by map
|
||||||
newCard.setDamage(c.getDamage());
|
newCard.setDamage(c.getDamage());
|
||||||
|
newCard.setDamageReceivedThisTurn(c.getDamageReceivedThisTurn());
|
||||||
|
|
||||||
newCard.setChangedCardColors(c.getChangedCardColorsTable());
|
newCard.setChangedCardColors(c.getChangedCardColorsTable());
|
||||||
newCard.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable());
|
newCard.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable());
|
||||||
|
|||||||
@@ -198,13 +198,10 @@ public class GameSimulator {
|
|||||||
final SpellAbility playingSa = sa;
|
final SpellAbility playingSa = sa;
|
||||||
|
|
||||||
simGame.copyLastState();
|
simGame.copyLastState();
|
||||||
boolean success = ComputerUtil.handlePlayingSpellAbility(aiPlayer, sa, simGame, new Runnable() {
|
boolean success = ComputerUtil.handlePlayingSpellAbility(aiPlayer, sa, simGame, () -> {
|
||||||
@Override
|
if (interceptor != null) {
|
||||||
public void run() {
|
interceptor.announceX(playingSa);
|
||||||
if (interceptor != null) {
|
interceptor.chooseTargets(playingSa, GameSimulator.this);
|
||||||
interceptor.announceX(playingSa);
|
|
||||||
interceptor.chooseTargets(playingSa, GameSimulator.this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.ai.simulation;
|
package forge.ai.simulation;
|
||||||
|
|
||||||
|
import forge.ai.ComputerUtilAbility;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -16,7 +17,7 @@ import forge.game.spellability.AbilitySub;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class SpellAbilityChoicesIterator {
|
public class SpellAbilityChoicesIterator {
|
||||||
private SimulationController controller;
|
private final SimulationController controller;
|
||||||
|
|
||||||
private Iterator<int[]> modeIterator;
|
private Iterator<int[]> modeIterator;
|
||||||
private int[] selectedModes;
|
private int[] selectedModes;
|
||||||
@@ -34,11 +35,13 @@ public class SpellAbilityChoicesIterator {
|
|||||||
Card selectedChoice;
|
Card selectedChoice;
|
||||||
Score bestScoreForChoice = new Score(Integer.MIN_VALUE);
|
Score bestScoreForChoice = new Score(Integer.MIN_VALUE);
|
||||||
}
|
}
|
||||||
private ArrayList<ChoicePoint> choicePoints = new ArrayList<>();
|
private final ArrayList<ChoicePoint> choicePoints = new ArrayList<>();
|
||||||
private int incrementedCpIndex = 0;
|
private int incrementedCpIndex = 0;
|
||||||
private int cpIndex = -1;
|
private int cpIndex = -1;
|
||||||
|
|
||||||
private int evalDepth;
|
private int evalDepth;
|
||||||
|
// Maps from filtered mode indexes to original ones.
|
||||||
|
private List<Integer> modesMap;
|
||||||
|
|
||||||
public SpellAbilityChoicesIterator(SimulationController controller) {
|
public SpellAbilityChoicesIterator(SimulationController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@@ -46,17 +49,28 @@ public class SpellAbilityChoicesIterator {
|
|||||||
|
|
||||||
public List<AbilitySub> chooseModesForAbility(List<AbilitySub> choices, int min, int num, boolean allowRepeat) {
|
public List<AbilitySub> chooseModesForAbility(List<AbilitySub> choices, int min, int num, boolean allowRepeat) {
|
||||||
if (modeIterator == null) {
|
if (modeIterator == null) {
|
||||||
// TODO: Need to skip modes that are invalid (e.g. targets don't exist)!
|
// Skip modes that don't have legal targets.
|
||||||
|
modesMap = new ArrayList<>();
|
||||||
|
int origIndex = -1;
|
||||||
|
for (AbilitySub sub : choices) {
|
||||||
|
origIndex++;
|
||||||
|
if (!ComputerUtilAbility.isFullyTargetable(sub)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
modesMap.add(origIndex);
|
||||||
|
}
|
||||||
// TODO: Do we need to do something special to support cards that have extra costs
|
// TODO: Do we need to do something special to support cards that have extra costs
|
||||||
// when choosing more modes, like Blessed Alliance?
|
// when choosing more modes, like Blessed Alliance?
|
||||||
if (!allowRepeat) {
|
if (modesMap.isEmpty()) {
|
||||||
modeIterator = CombinatoricsUtils.combinationsIterator(choices.size(), num);
|
return null;
|
||||||
|
} else if (!allowRepeat) {
|
||||||
|
modeIterator = CombinatoricsUtils.combinationsIterator(modesMap.size(), num);
|
||||||
} else {
|
} else {
|
||||||
// Note: When allowRepeat is true, it does result in many possibilities being tried.
|
// Note: When allowRepeat is true, it does result in many possibilities being tried.
|
||||||
// We should ideally prune some of those at a higher level.
|
// We should ideally prune some of those at a higher level.
|
||||||
modeIterator = new AllowRepeatModesIterator(choices.size(), min, num);
|
modeIterator = new AllowRepeatModesIterator(modesMap.size(), min, num);
|
||||||
}
|
}
|
||||||
selectedModes = modeIterator.next();
|
selectedModes = remapModes(modeIterator.next());
|
||||||
advancedToNextMode = true;
|
advancedToNextMode = true;
|
||||||
}
|
}
|
||||||
// Note: If modeIterator already existed, selectedModes would have been updated in advance().
|
// Note: If modeIterator already existed, selectedModes would have been updated in advance().
|
||||||
@@ -78,6 +92,13 @@ public class SpellAbilityChoicesIterator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int[] remapModes(int[] modes) {
|
||||||
|
for (int i = 0; i < modes.length; i++) {
|
||||||
|
modes[i] = modesMap.get(modes[i]);
|
||||||
|
}
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
|
||||||
public Card chooseCard(CardCollection fetchList) {
|
public Card chooseCard(CardCollection fetchList) {
|
||||||
cpIndex++;
|
cpIndex++;
|
||||||
if (cpIndex >= choicePoints.size()) {
|
if (cpIndex >= choicePoints.size()) {
|
||||||
@@ -86,8 +107,7 @@ public class SpellAbilityChoicesIterator {
|
|||||||
ChoicePoint cp = choicePoints.get(cpIndex);
|
ChoicePoint cp = choicePoints.get(cpIndex);
|
||||||
// Prune duplicates.
|
// Prune duplicates.
|
||||||
HashSet<String> uniqueCards = new HashSet<>();
|
HashSet<String> uniqueCards = new HashSet<>();
|
||||||
for (int i = 0; i < fetchList.size(); i++) {
|
for (Card card : fetchList) {
|
||||||
Card card = fetchList.get(i);
|
|
||||||
if (uniqueCards.add(card.getName()) && uniqueCards.size() == cp.nextChoice + 1) {
|
if (uniqueCards.add(card.getName()) && uniqueCards.size() == cp.nextChoice + 1) {
|
||||||
cp.selectedChoice = card;
|
cp.selectedChoice = card;
|
||||||
}
|
}
|
||||||
@@ -137,14 +157,9 @@ public class SpellAbilityChoicesIterator {
|
|||||||
evalDepth++;
|
evalDepth++;
|
||||||
pushTarget = false;
|
pushTarget = false;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getSelectModes() {
|
|
||||||
return selectedModes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean advance(Score lastScore) {
|
public boolean advance(Score lastScore) {
|
||||||
cpIndex = -1;
|
cpIndex = -1;
|
||||||
for (ChoicePoint cp : choicePoints) {
|
for (ChoicePoint cp : choicePoints) {
|
||||||
@@ -195,7 +210,7 @@ public class SpellAbilityChoicesIterator {
|
|||||||
doneEvaluating(bestScoreForMode);
|
doneEvaluating(bestScoreForMode);
|
||||||
bestScoreForMode = new Score(Integer.MIN_VALUE);
|
bestScoreForMode = new Score(Integer.MIN_VALUE);
|
||||||
if (modeIterator.hasNext()) {
|
if (modeIterator.hasNext()) {
|
||||||
selectedModes = modeIterator.next();
|
selectedModes = remapModes(modeIterator.next());
|
||||||
advancedToNextMode = true;
|
advancedToNextMode = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -232,8 +247,8 @@ public class SpellAbilityChoicesIterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class AllowRepeatModesIterator implements Iterator<int[]> {
|
private static class AllowRepeatModesIterator implements Iterator<int[]> {
|
||||||
private int numChoices;
|
private final int numChoices;
|
||||||
private int max;
|
private final int max;
|
||||||
private int[] indexes;
|
private int[] indexes;
|
||||||
|
|
||||||
public AllowRepeatModesIterator(int numChoices, int min, int max) {
|
public AllowRepeatModesIterator(int numChoices, int min, int max) {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class SpellAbilityPicker {
|
|||||||
private SpellAbilityChoicesIterator interceptor;
|
private SpellAbilityChoicesIterator interceptor;
|
||||||
|
|
||||||
private Plan plan;
|
private Plan plan;
|
||||||
|
private int numSimulations;
|
||||||
|
|
||||||
public SpellAbilityPicker(Game game, Player player) {
|
public SpellAbilityPicker(Game game, Player player) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
@@ -66,19 +67,7 @@ public class SpellAbilityPicker {
|
|||||||
private List<SpellAbility> getCandidateSpellsAndAbilities() {
|
private List<SpellAbility> getCandidateSpellsAndAbilities() {
|
||||||
CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player);
|
CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player);
|
||||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player);
|
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player);
|
||||||
CardCollection landsToPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
|
HashMap<String, Card> landsDeDupe = new HashMap<>();
|
||||||
if (landsToPlay != null) {
|
|
||||||
HashMap<String, Card> landsDeDupe = new HashMap<>();
|
|
||||||
for (Card land : landsToPlay) {
|
|
||||||
Card previousLand = landsDeDupe.get(land.getName());
|
|
||||||
// Skip identical lands.
|
|
||||||
if (previousLand != null && previousLand.getZone() == land.getZone() && previousLand.getOwner() == land.getOwner()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
landsDeDupe.put(land.getName(), land);
|
|
||||||
all.add(new LandAbility(land));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<SpellAbility> candidateSAs = ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player);
|
List<SpellAbility> candidateSAs = ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player);
|
||||||
int writeIndex = 0;
|
int writeIndex = 0;
|
||||||
for (int i = 0; i < candidateSAs.size(); i++) {
|
for (int i = 0; i < candidateSAs.size(); i++) {
|
||||||
@@ -86,6 +75,16 @@ public class SpellAbilityPicker {
|
|||||||
if (sa.isManaAbility()) {
|
if (sa.isManaAbility()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip identical lands.
|
||||||
|
if (sa instanceof LandAbility) {
|
||||||
|
Card land = sa.getHostCard();
|
||||||
|
Card previousLand = landsDeDupe.get(sa.getHostCard().getName());
|
||||||
|
if (previousLand != null && previousLand.getZone() == land.getZone() &&
|
||||||
|
previousLand.getOwner() == land.getOwner()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
landsDeDupe.put(land.getName(), land);
|
||||||
|
}
|
||||||
sa.setActivatingPlayer(player, true);
|
sa.setActivatingPlayer(player, true);
|
||||||
|
|
||||||
AiPlayDecision opinion = canPlayAndPayForSim(sa);
|
AiPlayDecision opinion = canPlayAndPayForSim(sa);
|
||||||
@@ -95,7 +94,7 @@ public class SpellAbilityPicker {
|
|||||||
|
|
||||||
if (opinion != AiPlayDecision.WillPlay)
|
if (opinion != AiPlayDecision.WillPlay)
|
||||||
continue;
|
continue;
|
||||||
candidateSAs.set(writeIndex, sa);
|
candidateSAs.set(writeIndex, sa);
|
||||||
writeIndex++;
|
writeIndex++;
|
||||||
}
|
}
|
||||||
candidateSAs.subList(writeIndex, candidateSAs.size()).clear();
|
candidateSAs.subList(writeIndex, candidateSAs.size()).clear();
|
||||||
@@ -353,7 +352,9 @@ public class SpellAbilityPicker {
|
|||||||
if (!ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
if (!ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||||
return AiPlayDecision.CantAfford;
|
return AiPlayDecision.CantAfford;
|
||||||
}
|
}
|
||||||
|
if (!ComputerUtilAbility.isFullyTargetable(sa)) {
|
||||||
|
return AiPlayDecision.TargetingFailed;
|
||||||
|
}
|
||||||
if (shouldWaitForLater(sa)) {
|
if (shouldWaitForLater(sa)) {
|
||||||
return AiPlayDecision.AnotherTime;
|
return AiPlayDecision.AnotherTime;
|
||||||
}
|
}
|
||||||
@@ -375,10 +376,13 @@ public class SpellAbilityPicker {
|
|||||||
final SpellAbilityChoicesIterator choicesIterator = new SpellAbilityChoicesIterator(controller);
|
final SpellAbilityChoicesIterator choicesIterator = new SpellAbilityChoicesIterator(controller);
|
||||||
Score lastScore;
|
Score lastScore;
|
||||||
do {
|
do {
|
||||||
|
// TODO: MyRandom should be an instance on the game object, so that we could do
|
||||||
|
// simulations in parallel without messing up global state.
|
||||||
MyRandom.setRandom(new Random(randomSeedToUse));
|
MyRandom.setRandom(new Random(randomSeedToUse));
|
||||||
GameSimulator simulator = new GameSimulator(controller, game, player, phase);
|
GameSimulator simulator = new GameSimulator(controller, game, player, phase);
|
||||||
simulator.setInterceptor(choicesIterator);
|
simulator.setInterceptor(choicesIterator);
|
||||||
lastScore = simulator.simulateSpellAbility(sa);
|
lastScore = simulator.simulateSpellAbility(sa);
|
||||||
|
numSimulations++;
|
||||||
if (lastScore.value > bestScore.value) {
|
if (lastScore.value > bestScore.value) {
|
||||||
bestScore = lastScore;
|
bestScore = lastScore;
|
||||||
}
|
}
|
||||||
@@ -462,4 +466,8 @@ public class SpellAbilityPicker {
|
|||||||
}
|
}
|
||||||
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), effect, amount, exclude);
|
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), effect, amount, exclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNumSimulations() {
|
||||||
|
return numSimulations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,6 +240,9 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
|||||||
// cannot still decide, if this "name|set" format is needed anymore
|
// cannot still decide, if this "name|set" format is needed anymore
|
||||||
// return String.format("%s|%s", name, cardSet);
|
// return String.format("%s|%s", name, cardSet);
|
||||||
}
|
}
|
||||||
|
public String getCardName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This (utility) method transform a collectorNumber String into a key string for sorting.
|
* This (utility) method transform a collectorNumber String into a key string for sorting.
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class Localizer {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Language '" + resourceBundle.toString() + "' loaded successfully.");
|
System.out.println("Language '" + resourceBundle.getBaseBundleName() + "' loaded successfully.");
|
||||||
|
|
||||||
notifyObservers();
|
notifyObservers();
|
||||||
|
|
||||||
|
|||||||
@@ -164,8 +164,8 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
c.clearControllers();
|
c.clearControllers();
|
||||||
if (c.removeChangedState()) {
|
if (cause != null) {
|
||||||
c.updateStateForView();
|
unanimateOnAbortedChange(cause, c);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@@ -365,7 +365,18 @@ public class GameAction {
|
|||||||
copied.getOwner().removeInboundToken(copied);
|
copied.getOwner().removeInboundToken(copied);
|
||||||
|
|
||||||
if (repres == ReplacementResult.Prevented) {
|
if (repres == ReplacementResult.Prevented) {
|
||||||
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard)) {
|
c.clearEtbCounters();
|
||||||
|
c.clearControllers();
|
||||||
|
if (cause != null) {
|
||||||
|
unanimateOnAbortedChange(cause, c);
|
||||||
|
if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) {
|
||||||
|
c.setBackSide(false);
|
||||||
|
c.changeToState(CardStateName.Original);
|
||||||
|
}
|
||||||
|
unattachCardLeavingBattlefield(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.isInZone(ZoneType.Stack) && !zoneTo.is(ZoneType.Graveyard)) {
|
||||||
return moveToGraveyard(c, cause, params);
|
return moveToGraveyard(c, cause, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,10 +384,8 @@ public class GameAction {
|
|||||||
copied.clearDelved();
|
copied.clearDelved();
|
||||||
copied.clearConvoked();
|
copied.clearConvoked();
|
||||||
copied.clearExploited();
|
copied.clearExploited();
|
||||||
}
|
} else if (toBattlefield && !c.isInPlay()) {
|
||||||
|
// was replaced with another Zone Change
|
||||||
// was replaced with another Zone Change
|
|
||||||
if (toBattlefield && !c.isInPlay()) {
|
|
||||||
if (c.removeChangedState()) {
|
if (c.removeChangedState()) {
|
||||||
c.updateStateForView();
|
c.updateStateForView();
|
||||||
}
|
}
|
||||||
@@ -570,6 +579,12 @@ public class GameAction {
|
|||||||
c.setZone(zoneTo);
|
c.setZone(zoneTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fromBattlefield) {
|
||||||
|
// order here is important so it doesn't unattach cards that might have returned from UntilHostLeavesPlay
|
||||||
|
unattachCardLeavingBattlefield(copied);
|
||||||
|
c.runLeavesPlayCommands();
|
||||||
|
}
|
||||||
|
|
||||||
// do ETB counters after zone add
|
// do ETB counters after zone add
|
||||||
if (!suppress && toBattlefield && !copied.getEtbCounters().isEmpty()) {
|
if (!suppress && toBattlefield && !copied.getEtbCounters().isEmpty()) {
|
||||||
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
||||||
@@ -697,7 +712,6 @@ public class GameAction {
|
|||||||
|
|
||||||
copied.setState(CardStateName.Original, true);
|
copied.setState(CardStateName.Original, true);
|
||||||
}
|
}
|
||||||
unattachCardLeavingBattlefield(copied);
|
|
||||||
} else if (toBattlefield) {
|
} else if (toBattlefield) {
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
|
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
|
||||||
@@ -973,9 +987,15 @@ public class GameAction {
|
|||||||
if (c.isInZone(ZoneType.Stack)) {
|
if (c.isInZone(ZoneType.Stack)) {
|
||||||
c.getGame().getStack().remove(c);
|
c.getGame().getStack().remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Zone z = c.getZone();
|
||||||
// in some corner cases there's no zone yet (copied spell that failed targeting)
|
// in some corner cases there's no zone yet (copied spell that failed targeting)
|
||||||
if (c.getZone() != null) {
|
if (z != null) {
|
||||||
c.getZone().remove(c);
|
z.remove(c);
|
||||||
|
if (z.is(ZoneType.Battlefield)) {
|
||||||
|
c.runLeavesPlayCommands();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CR 603.6c other players LTB triggers should work
|
// CR 603.6c other players LTB triggers should work
|
||||||
@@ -1036,20 +1056,13 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
for (Player p : game.getPlayers()) {
|
|
||||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
final int tiz = c.getTurnInZone();
|
|
||||||
|
|
||||||
oldBattlefield.remove(c);
|
oldBattlefield.remove(c);
|
||||||
newBattlefield.add(c);
|
newBattlefield.add(c);
|
||||||
c.setSickness(true);
|
|
||||||
if (game.getPhaseHandler().inCombat()) {
|
if (game.getPhaseHandler().inCombat()) {
|
||||||
game.getCombat().removeFromCombat(c);
|
game.getCombat().removeFromCombat(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setTurnInZone(tiz);
|
|
||||||
c.setCameUnderControlSinceLastUpkeep(true);
|
c.setCameUnderControlSinceLastUpkeep(true);
|
||||||
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||||
@@ -1057,9 +1070,6 @@ public class GameAction {
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams, false);
|
||||||
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
for (Player p : game.getPlayers()) {
|
|
||||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(true);
|
|
||||||
}
|
|
||||||
c.runChangeControllerCommands();
|
c.runChangeControllerCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2560,4 +2570,17 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void unanimateOnAbortedChange(final SpellAbility cause, final Card c) {
|
||||||
|
if (cause.hasParam("AnimateSubAbility")) {
|
||||||
|
long unanimateTimestamp = Long.valueOf(cause.getAdditionalAbility("AnimateSubAbility").getSVar("unanimateTimestamp"));
|
||||||
|
c.removeChangedCardKeywords(unanimateTimestamp, 0);
|
||||||
|
c.removeChangedCardTypes(unanimateTimestamp, 0);
|
||||||
|
c.removeChangedName(unanimateTimestamp, 0);
|
||||||
|
c.removeNewPT(unanimateTimestamp, 0);
|
||||||
|
if (c.removeChangedCardTraits(unanimateTimestamp, 0)) {
|
||||||
|
c.updateStateForView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -895,7 +895,7 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
CardCollection completeList = new CardCollection();
|
CardCollection completeList = new CardCollection();
|
||||||
PlayerCollection players = new PlayerCollection(game.getPlayers());
|
PlayerCollection players = new PlayerCollection(game.getPlayers());
|
||||||
// CR 613.7k use APNAP
|
// CR 613.7m use APNAP
|
||||||
int indexAP = players.indexOf(game.getPhaseHandler().getPlayerTurn());
|
int indexAP = players.indexOf(game.getPhaseHandler().getPlayerTurn());
|
||||||
if (indexAP != -1) {
|
if (indexAP != -1) {
|
||||||
Collections.rotate(players, - indexAP);
|
Collections.rotate(players, - indexAP);
|
||||||
|
|||||||
@@ -340,6 +340,13 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table, params);
|
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Pair<Integer, Boolean>> getDamageReceivedThisTurn() {
|
||||||
|
return damageReceivedThisTurn;
|
||||||
|
}
|
||||||
|
public void setDamageReceivedThisTurn(List<Pair<Integer, Boolean>> dmg) {
|
||||||
|
damageReceivedThisTurn.addAll(dmg);
|
||||||
|
}
|
||||||
|
|
||||||
public void receiveDamage(Pair<Integer, Boolean> dmg) {
|
public void receiveDamage(Pair<Integer, Boolean> dmg) {
|
||||||
damageReceivedThisTurn.add(dmg);
|
damageReceivedThisTurn.add(dmg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -511,12 +511,8 @@ public class AbilityUtils {
|
|||||||
} else if (hType.equals("Other")) {
|
} else if (hType.equals("Other")) {
|
||||||
players.addAll(player.getAllOtherPlayers());
|
players.addAll(player.getAllOtherPlayers());
|
||||||
val = playerXCount(players, calcX[1], card, ability);
|
val = playerXCount(players, calcX[1], card, ability);
|
||||||
} else if (hType.equals("Remembered")) {
|
} else if (hType.startsWith("Remembered")) {
|
||||||
for (final Object o : card.getRemembered()) {
|
addPlayer(card.getRemembered(), hType, players);
|
||||||
if (o instanceof Player) {
|
|
||||||
players.add((Player) o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val = playerXCount(players, calcX[1], card, ability);
|
val = playerXCount(players, calcX[1], card, ability);
|
||||||
} else if (hType.equals("NonActive")) {
|
} else if (hType.equals("NonActive")) {
|
||||||
players.addAll(game.getPlayers());
|
players.addAll(game.getPlayers());
|
||||||
|
|||||||
@@ -400,6 +400,23 @@ public abstract class SpellAbilityEffect {
|
|||||||
addedTrigger.setIntrinsic(true);
|
addedTrigger.setIntrinsic(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void addExileOnCastOrMoveTrigger(final Card card, final String zone) {
|
||||||
|
String trig = "Mode$ SpellCast | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True";
|
||||||
|
String effect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile";
|
||||||
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
|
||||||
|
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
||||||
|
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||||
|
addedTrigger.setIntrinsic(true);
|
||||||
|
//Any on Destination will cause the effect to remove itself when cancelling to play the card
|
||||||
|
String trig2 = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | Destination$ Hand,Library,Graveyard,Battlefield,Command,Sideboard | TriggerZones$ Command | Static$ True";
|
||||||
|
String effect2 = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile";
|
||||||
|
final Trigger parsedTrigger2 = TriggerHandler.parseTrigger(trig2, card, true);
|
||||||
|
parsedTrigger2.setOverridingAbility(AbilityFactory.getAbility(effect2, card));
|
||||||
|
final Trigger addedTrigger2 = card.addTrigger(parsedTrigger2);
|
||||||
|
addedTrigger2.setIntrinsic(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected static void addExileOnCounteredTrigger(final Card card) {
|
protected static void addExileOnCounteredTrigger(final Card card) {
|
||||||
String trig = "Mode$ Countered | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True";
|
String trig = "Mode$ Countered | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True";
|
||||||
String effect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile";
|
String effect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile";
|
||||||
@@ -434,7 +451,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) {
|
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = card.getGame();
|
final Game game = card.getGame();
|
||||||
final Card eff = createEffect(sa, sa.getActivatingPlayer(), host.getName() + "'s Effect", host.getImageKey());
|
final Card eff = createEffect(sa, sa.getActivatingPlayer(), host + "'s Effect", host.getImageKey());
|
||||||
|
|
||||||
addLeaveBattlefieldReplacement(eff, zone);
|
addLeaveBattlefieldReplacement(eff, zone);
|
||||||
|
|
||||||
|
|||||||
@@ -136,10 +136,10 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
CardCollectionView list;
|
CardCollectionView list;
|
||||||
|
|
||||||
if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
|
if (sa.usesTargeting() || sa.hasParam("Defined")) {
|
||||||
list = game.getCardsIn(ZoneType.Battlefield);
|
|
||||||
} else {
|
|
||||||
list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield);
|
list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield);
|
||||||
|
} else {
|
||||||
|
list = game.getCardsIn(ZoneType.Battlefield);
|
||||||
}
|
}
|
||||||
|
|
||||||
list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa);
|
list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa);
|
||||||
@@ -155,18 +155,18 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(c));
|
game.fireEvent(new GameEventCardStatsChanged(c));
|
||||||
|
|
||||||
final GameCommand unanimate = new GameCommand() {
|
|
||||||
private static final long serialVersionUID = -5861759814760561373L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
doUnanimate(c, timestamp);
|
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(c));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!permanent) {
|
if (!permanent) {
|
||||||
|
final GameCommand unanimate = new GameCommand() {
|
||||||
|
private static final long serialVersionUID = -5861759814760561373L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doUnanimate(c, timestamp);
|
||||||
|
|
||||||
|
game.fireEvent(new GameEventCardStatsChanged(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
addUntilCommand(sa, unanimate);
|
addUntilCommand(sa, unanimate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
|||||||
discardedMap.put(p, p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance));
|
discardedMap.put(p, p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance));
|
||||||
} else { // Battlefield
|
} else { // Battlefield
|
||||||
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||||
if ( null == card ) continue;
|
if (null == card) continue;
|
||||||
game.getAction().sacrifice(card, sa, true, table, params);
|
game.getAction().sacrifice(card, sa, true, table, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package forge.game.ability.effects;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
@@ -14,6 +12,7 @@ import forge.game.card.Card;
|
|||||||
import forge.game.event.GameEventCombatChanged;
|
import forge.game.event.GameEventCombatChanged;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
public class BecomesBlockedEffect extends SpellAbilityEffect {
|
public class BecomesBlockedEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -21,9 +20,7 @@ public class BecomesBlockedEffect extends SpellAbilityEffect {
|
|||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
final List<Card> tgtCards = getTargetCards(sa);
|
sb.append(Lang.joinHomogenous(getTargetCards(sa)));
|
||||||
|
|
||||||
sb.append(StringUtils.join(tgtCards, ", "));
|
|
||||||
sb.append(" becomes blocked.");
|
sb.append(" becomes blocked.");
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
@@ -19,6 +16,7 @@ import forge.game.player.Player;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.util.CardTranslation;
|
import forge.util.CardTranslation;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
@@ -28,10 +26,9 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
|||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
final List<Card> tgtCards = getTargetCards(sa);
|
|
||||||
// should update when adding effects for defined blocker
|
// should update when adding effects for defined blocker
|
||||||
sb.append("Reselect the defender of ");
|
sb.append("Reselect the defender of ");
|
||||||
sb.append(StringUtils.join(tgtCards, ", "));
|
sb.append(Lang.joinHomogenous(getTargetCards(sa)));
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
if (origin.contains(ZoneType.Library) && sa.hasParam("Search") && !sa.getActivatingPlayer().canSearchLibraryWith(sa, p)) {
|
if (origin.contains(ZoneType.Library) && sa.hasParam("Search") && !sa.getActivatingPlayer().canSearchLibraryWith(sa, p)) {
|
||||||
cards.removeAll(p.getCardsIn(ZoneType.Library));
|
cards.removeAll(p.getCardsIn(ZoneType.Library));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (origin.contains(ZoneType.Library) && sa.hasParam("Search")) {
|
if (origin.contains(ZoneType.Library) && sa.hasParam("Search")) {
|
||||||
// Search library using changezoneall effect need a param "Search"
|
// Search library using changezoneall effect need a param "Search"
|
||||||
@@ -95,8 +94,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
if (!libCards.isEmpty()) {
|
if (!libCards.isEmpty()) {
|
||||||
sa.getActivatingPlayer().getController().reveal(libCards, ZoneType.Library, libCards.get(0).getOwner());
|
sa.getActivatingPlayer().getController().reveal(libCards, ZoneType.Library, libCards.get(0).getOwner());
|
||||||
}
|
}
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(sa.getActivatingPlayer());
|
||||||
runParams.put(AbilityKey.Player, sa.getActivatingPlayer());
|
|
||||||
runParams.put(AbilityKey.Target, tgtPlayers);
|
runParams.put(AbilityKey.Target, tgtPlayers);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.SearchedLibrary, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.SearchedLibrary, runParams, false);
|
||||||
}
|
}
|
||||||
@@ -172,9 +170,11 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
// need LKI before Animate does apply
|
// need LKI before Animate does apply
|
||||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||||
|
|
||||||
|
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
|
||||||
source.addRemembered(c);
|
source.addRemembered(c);
|
||||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
AbilityUtils.resolve(animate);
|
||||||
source.removeRemembered(c);
|
source.removeRemembered(c);
|
||||||
|
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
|
||||||
}
|
}
|
||||||
if (sa.hasParam("Tapped")) {
|
if (sa.hasParam("Tapped")) {
|
||||||
c.setTapped(true);
|
c.setTapped(true);
|
||||||
|
|||||||
@@ -133,10 +133,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("ExileFaceDown")) {
|
if (sa.hasParam("ExileFaceDown")) {
|
||||||
sb.append(" face down");
|
sb.append(" face down");
|
||||||
}
|
}
|
||||||
|
sb.append(".");
|
||||||
} else if (destination.equals("Ante")) {
|
} else if (destination.equals("Ante")) {
|
||||||
sb.append("Add the top card of your library to the ante");
|
sb.append("Add the top card of your library to the ante.");
|
||||||
}
|
}
|
||||||
sb.append(".");
|
|
||||||
} else if (origin.equals("Library")) {
|
} else if (origin.equals("Library")) {
|
||||||
final boolean originAlt = sa.hasParam("OriginAlternative");
|
final boolean originAlt = sa.hasParam("OriginAlternative");
|
||||||
sb.append(chooserNames).append(" search").append(choosers.size() > 1 ? " " : "es ");
|
sb.append(chooserNames).append(" search").append(choosers.size() > 1 ? " " : "es ");
|
||||||
@@ -575,6 +575,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa);
|
movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa);
|
||||||
} else {
|
} else {
|
||||||
if (destination.equals(ZoneType.Battlefield)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
|
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||||
|
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||||
|
if (sa.isReplacementAbility()) {
|
||||||
|
ReplacementEffect re = sa.getReplacementEffect();
|
||||||
|
moveParams.put(AbilityKey.ReplacementEffect, re);
|
||||||
|
if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) {
|
||||||
|
moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Tapped") || sa.isNinjutsu()) {
|
if (sa.hasParam("Tapped") || sa.isNinjutsu()) {
|
||||||
gameCard.setTapped(true);
|
gameCard.setTapped(true);
|
||||||
}
|
}
|
||||||
@@ -583,6 +594,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
if (sa.hasParam("Transformed")) {
|
if (sa.hasParam("Transformed")) {
|
||||||
if (gameCard.isDoubleFaced()) {
|
if (gameCard.isDoubleFaced()) {
|
||||||
|
// need LKI before Animate does apply
|
||||||
|
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
|
||||||
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard));
|
||||||
|
}
|
||||||
gameCard.changeCardState("Transform", null, sa);
|
gameCard.changeCardState("Transform", null, sa);
|
||||||
} else {
|
} else {
|
||||||
// If it can't Transform, don't change zones.
|
// If it can't Transform, don't change zones.
|
||||||
@@ -650,26 +665,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
|
||||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
|
||||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
|
||||||
if (sa.isReplacementAbility()) {
|
|
||||||
ReplacementEffect re = sa.getReplacementEffect();
|
|
||||||
moveParams.put(AbilityKey.ReplacementEffect, re);
|
|
||||||
if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) {
|
|
||||||
moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||||
// need LKI before Animate does apply
|
// need LKI before Animate does apply
|
||||||
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
|
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
|
||||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard));
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
|
||||||
hostCard.addRemembered(gameCard);
|
hostCard.addRemembered(gameCard);
|
||||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
AbilityUtils.resolve(animate);
|
||||||
hostCard.removeRemembered(gameCard);
|
hostCard.removeRemembered(gameCard);
|
||||||
|
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
|
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
|
||||||
@@ -776,7 +782,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("TrackDiscarded")) {
|
if (sa.hasParam("TrackDiscarded")) {
|
||||||
movedCard.setMadnessWithoutCast(true);
|
movedCard.setDiscarded(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1109,7 +1115,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
fetchList = (CardCollection)AbilityUtils.filterListByType(fetchList, sa.getParam("ChangeType"), sa);
|
fetchList = (CardCollection)AbilityUtils.filterListByType(fetchList, sa.getParam("ChangeType"), sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("NoShuffle")) {
|
if (sa.hasParam("NoShuffle") || "False".equals(sa.getParam("Shuffle"))) {
|
||||||
shuffleMandatory = false;
|
shuffleMandatory = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1249,7 +1255,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
CardLists.shuffle(chosenCards);
|
CardLists.shuffle(chosenCards);
|
||||||
}
|
}
|
||||||
// do not shuffle the library once we have placed a fetched card on top.
|
// do not shuffle the library once we have placed a fetched card on top.
|
||||||
if (origin.contains(ZoneType.Library) && (destination == ZoneType.Library) && !"False".equals(sa.getParam("Shuffle"))) {
|
if (origin.contains(ZoneType.Library) && (destination == ZoneType.Library) && shuffleMandatory) {
|
||||||
player.shuffle(sa);
|
player.shuffle(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1310,9 +1316,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
// need LKI before Animate does apply
|
// need LKI before Animate does apply
|
||||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||||
|
|
||||||
|
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
|
||||||
source.addRemembered(c);
|
source.addRemembered(c);
|
||||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
AbilityUtils.resolve(animate);
|
||||||
source.removeRemembered(c);
|
source.removeRemembered(c);
|
||||||
|
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
|
||||||
}
|
}
|
||||||
if (sa.hasParam("GainControl")) {
|
if (sa.hasParam("GainControl")) {
|
||||||
final String g = sa.getParam("GainControl");
|
final String g = sa.getParam("GainControl");
|
||||||
@@ -1331,6 +1339,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
if (sa.hasParam("Transformed")) {
|
if (sa.hasParam("Transformed")) {
|
||||||
if (c.isDoubleFaced()) {
|
if (c.isDoubleFaced()) {
|
||||||
|
// need LKI before Animate does apply
|
||||||
|
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
|
||||||
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||||
|
}
|
||||||
c.changeCardState("Transform", null, sa);
|
c.changeCardState("Transform", null, sa);
|
||||||
} else {
|
} else {
|
||||||
// If it can't Transform, don't change zones.
|
// If it can't Transform, don't change zones.
|
||||||
@@ -1390,7 +1402,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
movedCard.setTimestamp(ts);
|
movedCard.setTimestamp(ts);
|
||||||
|
|
||||||
if (sa.hasParam("AttachAfter") && movedCard.isAttachment()) {
|
if (sa.hasParam("AttachAfter") && movedCard.isAttachment() && movedCard.isInPlay()) {
|
||||||
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa);
|
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa);
|
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa);
|
||||||
|
|||||||
@@ -25,19 +25,15 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
List<String> restriction = null;
|
List<String> restriction = null;
|
||||||
|
|
||||||
if (sa.hasParam("ChoiceRestriction")) {
|
if (sa.hasParam("ChoiceRestriction")) {
|
||||||
String rest = sa.getParam("ChoiceRestriction");
|
restriction = source.getChosenModes(sa, sa.getParam("ChoiceRestriction"));
|
||||||
if (rest.equals("ThisGame")) {
|
|
||||||
restriction = source.getChosenModesGame(sa);
|
|
||||||
} else if (rest.equals("ThisTurn")) {
|
|
||||||
restriction = source.getChosenModesTurn(sa);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AbilitySub> choices = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
|
List<AbilitySub> choices = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
|
||||||
List<AbilitySub> toRemove = Lists.newArrayList();
|
List<AbilitySub> toRemove = Lists.newArrayList();
|
||||||
for (AbilitySub ch : choices) {
|
for (AbilitySub ch : choices) {
|
||||||
// 603.3c If one of the modes would be illegal, that mode can't be chosen.
|
// 603.3c If one of the modes would be illegal, that mode can't be chosen.
|
||||||
if ((ch.usesTargeting() && ch.isTrigger() && ch.getTargetRestrictions().getNumCandidates(ch, true) == 0) ||
|
if ((ch.usesTargeting() && ch.isTrigger() && ch.getMinTargets() > 0 &&
|
||||||
|
ch.getTargetRestrictions().getNumCandidates(ch, true) == 0) ||
|
||||||
(restriction != null && restriction.contains(ch.getDescription()))) {
|
(restriction != null && restriction.contains(ch.getDescription()))) {
|
||||||
toRemove.add(ch);
|
toRemove.add(ch);
|
||||||
}
|
}
|
||||||
@@ -97,6 +93,8 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
sb.append(" that hasn't been chosen");
|
sb.append(" that hasn't been chosen");
|
||||||
} else if (rest.equals("ThisTurn")) {
|
} else if (rest.equals("ThisTurn")) {
|
||||||
sb.append(" that hasn't been chosen this turn");
|
sb.append(" that hasn't been chosen this turn");
|
||||||
|
} else if (rest.equals("YourLastCombat")) {
|
||||||
|
sb.append(" that wasn't chosen during your last combat");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
List<Player> tgtPlayers = getTargetPlayers(sa);
|
List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
return TextUtil.concatWithSpace(sa.getActivatingPlayer().toString(),"controls", Lang.joinHomogenous(tgtPlayers),"during their next turn");
|
return TextUtil.concatWithSpace(sa.getActivatingPlayer().toString(), "controls", Lang.joinHomogenous(tgtPlayers), "during their next turn");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
final Player pl = !sa.hasParam("DefinedPlayer") ? sa.getActivatingPlayer() :
|
final Player pl = sa.hasParam("DefinedPlayer") ?
|
||||||
AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("DefinedPlayer"), sa).getFirst();
|
AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("DefinedPlayer"), sa).getFirst()
|
||||||
|
: sa.getActivatingPlayer();
|
||||||
sb.append(pl.getName());
|
sb.append(pl.getName());
|
||||||
|
|
||||||
if (sa.hasParam("CounterType")) {
|
if (sa.hasParam("CounterType")) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import forge.game.player.PlayerController;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.Zone;
|
import forge.game.zone.Zone;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
public class CountersRemoveEffect extends SpellAbilityEffect {
|
public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||||
@@ -53,13 +54,9 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
sb.append(" from");
|
sb.append(" from");
|
||||||
|
|
||||||
for (final Card c : getTargetCards(sa)) {
|
sb.append(Lang.joinHomogenous(getTargetCards(sa)));
|
||||||
sb.append(" ").append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Player tgtPlayer : getTargetPlayers(sa)) {
|
sb.append(Lang.joinHomogenous(getTargetPlayers(sa)));
|
||||||
sb.append(" ").append(tgtPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(".");
|
sb.append(".");
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import forge.game.card.CardLists;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.Lang;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
|
|
||||||
public class DamageEachEffect extends DamageBaseEffect {
|
public class DamageEachEffect extends DamageBaseEffect {
|
||||||
@@ -38,9 +39,7 @@ public class DamageEachEffect extends DamageBaseEffect {
|
|||||||
sb.append(sa.getParam("StackDescription"));
|
sb.append(sa.getParam("StackDescription"));
|
||||||
} else {
|
} else {
|
||||||
sb.append("Each ").append(desc).append(" deals ").append(dmg).append(" to ");
|
sb.append("Each ").append(desc).append(" deals ").append(dmg).append(" to ");
|
||||||
for (final Player p : getTargetPlayers(sa)) {
|
Lang.joinHomogenous(getTargetPlayers(sa));
|
||||||
sb.append(p);
|
|
||||||
}
|
|
||||||
if (sa.hasParam("DefinedCards")) {
|
if (sa.hasParam("DefinedCards")) {
|
||||||
if (sa.getParam("DefinedCards").equals("Self")) {
|
if (sa.getParam("DefinedCards").equals("Self")) {
|
||||||
sb.append(" itself");
|
sb.append(" itself");
|
||||||
|
|||||||
@@ -417,13 +417,13 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||||
// need LKI before Animate does apply
|
// need LKI before Animate does apply
|
||||||
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
|
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
|
||||||
host.addRemembered(c);
|
host.addRemembered(c);
|
||||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
AbilityUtils.resolve(animate);
|
||||||
host.removeRemembered(c);
|
host.removeRemembered(c);
|
||||||
|
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
|
||||||
}
|
}
|
||||||
c = game.getAction().moveTo(zone, c, sa, moveParams);
|
c = game.getAction().moveTo(zone, c, sa, moveParams);
|
||||||
if (destZone1.equals(ZoneType.Battlefield)) {
|
if (destZone1.equals(ZoneType.Battlefield)) {
|
||||||
|
|||||||
@@ -77,10 +77,20 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
|||||||
if (validMap.isEmpty()) {
|
if (validMap.isEmpty()) {
|
||||||
chooser.getController().notifyOfValue(sa, null, Localizer.getInstance().getMessage("lblNoValidCards"));
|
chooser.getController().notifyOfValue(sa, null, Localizer.getInstance().getMessage("lblNoValidCards"));
|
||||||
} else {
|
} else {
|
||||||
CardCollection chosen = chooser.getController().chooseCardsForEffectMultiple(validMap, sa, Localizer.getInstance().getMessage("lblChooseCards"), chooseOptional);
|
CardCollection chosen;
|
||||||
|
//ensure choosing something when possible and not optional
|
||||||
|
while (true) {
|
||||||
|
chosen = chooser.getController().chooseCardsForEffectMultiple(validMap, sa,
|
||||||
|
Localizer.getInstance().getMessage("lblChooseCards"), chooseOptional);
|
||||||
|
|
||||||
if (!chosen.isEmpty()) {
|
if (!chosen.isEmpty()) {
|
||||||
game.getAction().reveal(chosen, chooser, true, Localizer.getInstance().getMessage("lblPlayerPickedCardFrom", chooser.getName()));
|
game.getAction().reveal(chosen, chooser, true,
|
||||||
|
Localizer.getInstance().getMessage("lblPlayerPickedCardFrom", chooser.getName()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (chooseOptional) break;
|
||||||
|
chooser.getController().notifyOfValue(sa, null,
|
||||||
|
Localizer.getInstance().getMessage("lblMustChoose"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ChooseAmount") || sa.hasParam("ChosenZone")) {
|
if (sa.hasParam("ChooseAmount") || sa.hasParam("ChosenZone")) {
|
||||||
@@ -108,21 +118,23 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
|||||||
final ZoneType origin = c.getZone().getZoneType();
|
final ZoneType origin = c.getZone().getZoneType();
|
||||||
final PlayerZone zone = c.getOwner().getZone(destZone1);
|
final PlayerZone zone = c.getOwner().getZone(destZone1);
|
||||||
|
|
||||||
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
|
if (!sa.hasParam("ChangeLater")) {
|
||||||
if (libraryPosition == -1 || libraryPosition > zone.size()) {
|
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
|
||||||
libraryPosition = zone.size();
|
if (libraryPosition == -1 || libraryPosition > zone.size()) {
|
||||||
}
|
libraryPosition = zone.size();
|
||||||
c = game.getAction().moveTo(zone, c, libraryPosition, sa);
|
|
||||||
} else {
|
|
||||||
if (destZone1.equals(ZoneType.Battlefield)) {
|
|
||||||
if (sa.hasParam("Tapped")) {
|
|
||||||
c.setTapped(true);
|
|
||||||
}
|
}
|
||||||
|
c = game.getAction().moveTo(zone, c, libraryPosition, sa);
|
||||||
|
} else {
|
||||||
|
if (destZone1.equals(ZoneType.Battlefield)) {
|
||||||
|
if (sa.hasParam("Tapped")) {
|
||||||
|
c.setTapped(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = game.getAction().moveTo(zone, c, sa);
|
||||||
|
}
|
||||||
|
if (!origin.equals(c.getZone().getZoneType())) {
|
||||||
|
table.put(origin, c.getZone().getZoneType(), c);
|
||||||
}
|
}
|
||||||
c = game.getAction().moveTo(zone, c, sa);
|
|
||||||
}
|
|
||||||
if (!origin.equals(c.getZone().getZoneType())) {
|
|
||||||
table.put(origin, c.getZone().getZoneType(), c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ExileFaceDown")) {
|
if (sa.hasParam("ExileFaceDown")) {
|
||||||
@@ -142,38 +154,45 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now, move the rest to destZone2
|
// now, move the rest to destZone2
|
||||||
if (destZone2 == ZoneType.Library || destZone2 == ZoneType.PlanarDeck || destZone2 == ZoneType.SchemeDeck
|
if (!sa.hasParam("ChangeLater")) {
|
||||||
|| destZone2 == ZoneType.Graveyard) {
|
if (destZone2 == ZoneType.Library || destZone2 == ZoneType.PlanarDeck
|
||||||
CardCollection afterOrder = rest;
|
|| destZone2 == ZoneType.SchemeDeck || destZone2 == ZoneType.Graveyard) {
|
||||||
if (sa.hasParam("RestRandomOrder")) {
|
CardCollection afterOrder = rest;
|
||||||
CardLists.shuffle(afterOrder);
|
if (sa.hasParam("RestRandomOrder")) {
|
||||||
}
|
CardLists.shuffle(afterOrder);
|
||||||
if (libraryPosition2 != -1) {
|
|
||||||
// Closest to top
|
|
||||||
Collections.reverse(afterOrder);
|
|
||||||
}
|
|
||||||
for (final Card c : afterOrder) {
|
|
||||||
final ZoneType origin = c.getZone().getZoneType();
|
|
||||||
Card m;
|
|
||||||
if (destZone2 == ZoneType.Library) {
|
|
||||||
m = game.getAction().moveToLibrary(c, libraryPosition2, sa);
|
|
||||||
} else {
|
|
||||||
m = game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2, sa);
|
|
||||||
}
|
}
|
||||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
if (libraryPosition2 != -1) {
|
||||||
table.put(origin, m.getZone().getZoneType(), m);
|
// Closest to top
|
||||||
|
Collections.reverse(afterOrder);
|
||||||
|
}
|
||||||
|
for (final Card c : afterOrder) {
|
||||||
|
final ZoneType origin = c.getZone().getZoneType();
|
||||||
|
Card m;
|
||||||
|
if (destZone2 == ZoneType.Library) {
|
||||||
|
m = game.getAction().moveToLibrary(c, libraryPosition2, sa);
|
||||||
|
} else {
|
||||||
|
m = game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2, sa);
|
||||||
|
}
|
||||||
|
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||||
|
table.put(origin, m.getZone().getZoneType(), m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// just move them randomly
|
||||||
|
for (int i = 0; i < rest.size(); i++) {
|
||||||
|
Card c = rest.get(i);
|
||||||
|
final ZoneType origin = c.getZone().getZoneType();
|
||||||
|
final PlayerZone toZone = c.getOwner().getZone(destZone2);
|
||||||
|
c = game.getAction().moveTo(toZone, c, sa);
|
||||||
|
if (!origin.equals(c.getZone().getZoneType())) {
|
||||||
|
table.put(origin, c.getZone().getZoneType(), c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// just move them randomly
|
if (sa.hasParam("ImprintRest")) {
|
||||||
for (int i = 0; i < rest.size(); i++) {
|
for (Card c : rest) {
|
||||||
Card c = rest.get(i);
|
host.addImprintedCard(c);
|
||||||
final ZoneType origin = c.getZone().getZoneType();
|
|
||||||
final PlayerZone toZone = c.getOwner().getZone(destZone2);
|
|
||||||
c = game.getAction().moveTo(toZone, c, sa);
|
|
||||||
if (!origin.equals(c.getZone().getZoneType())) {
|
|
||||||
table.put(origin, c.getZone().getZoneType(), c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,6 +227,8 @@ public class EffectEffect extends SpellAbilityEffect {
|
|||||||
addForgetOnCastTrigger(eff);
|
addForgetOnCastTrigger(eff);
|
||||||
} else if (sa.hasParam("ExileOnMoved")) {
|
} else if (sa.hasParam("ExileOnMoved")) {
|
||||||
addExileOnMovedTrigger(eff, sa.getParam("ExileOnMoved"));
|
addExileOnMovedTrigger(eff, sa.getParam("ExileOnMoved"));
|
||||||
|
} else if (sa.hasParam("ExileOnCast")) {
|
||||||
|
addExileOnCastOrMoveTrigger(eff, sa.getParam("ExileOnCast"));
|
||||||
}
|
}
|
||||||
if (sa.hasParam("ForgetOnPhasedIn")) {
|
if (sa.hasParam("ForgetOnPhasedIn")) {
|
||||||
addForgetOnPhasedInTrigger(eff);
|
addForgetOnPhasedInTrigger(eff);
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
@@ -12,6 +8,7 @@ import forge.game.card.CardCollection;
|
|||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
public class RemoveFromCombatEffect extends SpellAbilityEffect {
|
public class RemoveFromCombatEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -19,10 +16,8 @@ public class RemoveFromCombatEffect extends SpellAbilityEffect {
|
|||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
final List<Card> tgtCards = getTargetCards(sa);
|
|
||||||
|
|
||||||
sb.append("Remove ");
|
sb.append("Remove ");
|
||||||
sb.append(StringUtils.join(tgtCards, ", "));
|
sb.append(Lang.joinHomogenous(getTargetCards(sa)));
|
||||||
sb.append(" from combat.");
|
sb.append(" from combat.");
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private boolean tributed = false;
|
private boolean tributed = false;
|
||||||
private boolean embalmed = false;
|
private boolean embalmed = false;
|
||||||
private boolean eternalized = false;
|
private boolean eternalized = false;
|
||||||
private boolean madnessWithoutCast = false;
|
private boolean discarded = false;
|
||||||
|
|
||||||
private boolean flipped = false;
|
private boolean flipped = false;
|
||||||
private boolean facedown = false;
|
private boolean facedown = false;
|
||||||
@@ -324,6 +324,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private SpellAbility[] basicLandAbilities = new SpellAbility[MagicColor.WUBRG.length];
|
private SpellAbility[] basicLandAbilities = new SpellAbility[MagicColor.WUBRG.length];
|
||||||
|
|
||||||
private int planeswalkerAbilityActivated;
|
private int planeswalkerAbilityActivated;
|
||||||
|
private boolean planeswalkerActivationLimitUsed;
|
||||||
|
|
||||||
private final ActivationTable numberTurnActivations = new ActivationTable();
|
private final ActivationTable numberTurnActivations = new ActivationTable();
|
||||||
private final ActivationTable numberGameActivations = new ActivationTable();
|
private final ActivationTable numberGameActivations = new ActivationTable();
|
||||||
@@ -331,9 +332,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
private final Map<SpellAbility, List<String>> chosenModesTurn = Maps.newHashMap();
|
private final Map<SpellAbility, List<String>> chosenModesTurn = Maps.newHashMap();
|
||||||
private final Map<SpellAbility, List<String>> chosenModesGame = Maps.newHashMap();
|
private final Map<SpellAbility, List<String>> chosenModesGame = Maps.newHashMap();
|
||||||
|
private final Map<SpellAbility, List<String>> chosenModesYourCombat = Maps.newHashMap();
|
||||||
|
private final Map<SpellAbility, List<String>> chosenModesYourLastCombat = Maps.newHashMap();
|
||||||
|
|
||||||
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
|
||||||
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
|
||||||
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesYourCombatStatic = HashBasedTable.create();
|
||||||
|
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesYourLastCombatStatic = HashBasedTable.create();
|
||||||
|
|
||||||
private CombatLki combatLKI;
|
private CombatLki combatLKI;
|
||||||
|
|
||||||
@@ -3263,7 +3268,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public final boolean isFlipped() {
|
public final boolean isFlipped() {
|
||||||
return flipped;
|
return flipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setFlipped(boolean value) {
|
public final void setFlipped(boolean value) {
|
||||||
flipped = value;
|
flipped = value;
|
||||||
}
|
}
|
||||||
@@ -3271,7 +3275,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public final void setCanCounter(final boolean b) {
|
public final void setCanCounter(final boolean b) {
|
||||||
canCounter = b;
|
canCounter = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean getCanCounter() {
|
public final boolean getCanCounter() {
|
||||||
return canCounter;
|
return canCounter;
|
||||||
}
|
}
|
||||||
@@ -5458,13 +5461,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
damageHistory = history;
|
damageHistory = history;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Pair<Integer, Boolean>> getDamageReceivedThisTurn() {
|
|
||||||
return damageReceivedThisTurn;
|
|
||||||
}
|
|
||||||
public void setDamageReceivedThisTurn(List<Pair<Integer, Boolean>> dmg) {
|
|
||||||
damageReceivedThisTurn.addAll(dmg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean hasDealtDamageToOpponentThisTurn() {
|
public final boolean hasDealtDamageToOpponentThisTurn() {
|
||||||
return getDamageHistory().getDamageDoneThisTurn(null, true, null, "Player.Opponent", this, getController(), null) > 0;
|
return getDamageHistory().getDamageDoneThisTurn(null, true, null, "Player.Opponent", this, getController(), null) > 0;
|
||||||
}
|
}
|
||||||
@@ -5805,8 +5801,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
return getCastSA().isMadness();
|
return getCastSA().isMadness();
|
||||||
}
|
}
|
||||||
public boolean getMadnessWithoutCast() { return madnessWithoutCast; }
|
public boolean wasDiscarded() { return discarded; }
|
||||||
public void setMadnessWithoutCast(boolean state) { madnessWithoutCast = state; }
|
public void setDiscarded(boolean state) { discarded = state; }
|
||||||
|
|
||||||
public final boolean isMonstrous() {
|
public final boolean isMonstrous() {
|
||||||
return monstrous;
|
return monstrous;
|
||||||
@@ -6345,6 +6341,18 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return isInZone(ZoneType.Battlefield);
|
return isInZone(ZoneType.Battlefield);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onEndOfCombat(final Player active) {
|
||||||
|
if (this.getController().equals(active)) {
|
||||||
|
chosenModesYourLastCombat.clear();
|
||||||
|
chosenModesYourLastCombatStatic.clear();
|
||||||
|
chosenModesYourLastCombat.putAll(chosenModesYourCombat);
|
||||||
|
chosenModesYourLastCombatStatic.putAll(chosenModesYourCombatStatic);
|
||||||
|
chosenModesYourCombat.clear();
|
||||||
|
chosenModesYourCombatStatic.clear();
|
||||||
|
updateAbilityTextForView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onCleanupPhase(final Player turn) {
|
public void onCleanupPhase(final Player turn) {
|
||||||
if (!this.hasKeyword("Damage isn't removed from CARDNAME during cleanup steps.")) {
|
if (!this.hasKeyword("Damage isn't removed from CARDNAME during cleanup steps.")) {
|
||||||
setDamage(0);
|
setDamage(0);
|
||||||
@@ -7037,7 +7045,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
numberAbilityResolved.clear();
|
numberAbilityResolved.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getChosenModesTurn(SpellAbility ability) {
|
public List<String> getChosenModes(SpellAbility ability, String type) {
|
||||||
SpellAbility original = null;
|
SpellAbility original = null;
|
||||||
SpellAbility root = ability.getRootAbility();
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|
||||||
@@ -7051,32 +7059,26 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ability.getGrantorStatic() != null) {
|
if (type.equals("ThisTurn")) {
|
||||||
return chosenModesTurnStatic.get(original, ability.getGrantorStatic());
|
if (ability.getGrantorStatic() != null) {
|
||||||
}
|
return chosenModesTurnStatic.get(original, ability.getGrantorStatic());
|
||||||
return chosenModesTurn.get(original);
|
|
||||||
}
|
|
||||||
public List<String> getChosenModesGame(SpellAbility ability) {
|
|
||||||
SpellAbility original = null;
|
|
||||||
SpellAbility root = ability.getRootAbility();
|
|
||||||
|
|
||||||
// because trigger spell abilities are copied, try to get original one
|
|
||||||
if (root.isTrigger()) {
|
|
||||||
original = root.getTrigger().getOverridingAbility();
|
|
||||||
} else {
|
|
||||||
original = ability.getOriginalAbility();
|
|
||||||
if (original == null) {
|
|
||||||
original = ability;
|
|
||||||
}
|
}
|
||||||
|
return chosenModesTurn.get(original);
|
||||||
|
} else if (type.equals("ThisGame")) {
|
||||||
|
if (ability.getGrantorStatic() != null) {
|
||||||
|
return chosenModesGameStatic.get(original, ability.getGrantorStatic());
|
||||||
|
}
|
||||||
|
return chosenModesGame.get(original);
|
||||||
|
} else if (type.equals("YourLastCombat")) {
|
||||||
|
if (ability.getGrantorStatic() != null) {
|
||||||
|
return chosenModesYourLastCombatStatic.get(original, ability.getGrantorStatic());
|
||||||
|
}
|
||||||
|
return chosenModesYourLastCombat.get(original);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
if (ability.getGrantorStatic() != null) {
|
|
||||||
return chosenModesGameStatic.get(original, ability.getGrantorStatic());
|
|
||||||
}
|
|
||||||
return chosenModesGame.get(original);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addChosenModes(SpellAbility ability, String mode) {
|
public void addChosenModes(SpellAbility ability, String mode, boolean yourCombat) {
|
||||||
SpellAbility original = null;
|
SpellAbility original = null;
|
||||||
SpellAbility root = ability.getRootAbility();
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|
||||||
@@ -7103,6 +7105,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
chosenModesGameStatic.put(original, ability.getGrantorStatic(), result);
|
chosenModesGameStatic.put(original, ability.getGrantorStatic(), result);
|
||||||
}
|
}
|
||||||
result.add(mode);
|
result.add(mode);
|
||||||
|
if (yourCombat) {
|
||||||
|
result = chosenModesYourCombatStatic.get(original, ability.getGrantorStatic());
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesYourCombatStatic.put(original, ability.getGrantorStatic(), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
List<String> result = chosenModesTurn.get(original);
|
List<String> result = chosenModesTurn.get(original);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
@@ -7117,6 +7126,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
chosenModesGame.put(original, result);
|
chosenModesGame.put(original, result);
|
||||||
}
|
}
|
||||||
result.add(mode);
|
result.add(mode);
|
||||||
|
|
||||||
|
if (yourCombat) {
|
||||||
|
result = chosenModesYourCombat.get(original);
|
||||||
|
if (result == null) {
|
||||||
|
result = Lists.newArrayList();
|
||||||
|
chosenModesYourCombat.put(original, result);
|
||||||
|
}
|
||||||
|
result.add(mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7130,11 +7148,19 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addPlaneswalkerAbilityActivated() {
|
public void addPlaneswalkerAbilityActivated() {
|
||||||
planeswalkerAbilityActivated++;
|
// track if increased limit was used for activation because if there are also additional ones they can count on top
|
||||||
|
if (++planeswalkerAbilityActivated == 2 && StaticAbilityNumLoyaltyAct.limitIncrease(this)) {
|
||||||
|
planeswalkerActivationLimitUsed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean planeswalkerActivationLimitUsed() {
|
||||||
|
return planeswalkerActivationLimitUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetActivationsPerTurn() {
|
public void resetActivationsPerTurn() {
|
||||||
planeswalkerAbilityActivated = 0;
|
planeswalkerAbilityActivated = 0;
|
||||||
|
planeswalkerActivationLimitUsed = false;
|
||||||
numberTurnActivations.clear();
|
numberTurnActivations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3855,7 +3855,7 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
SpellAbility saExile = AbilityFactory.getAbility(abExile, card);
|
SpellAbility saExile = AbilityFactory.getAbility(abExile, card);
|
||||||
|
|
||||||
String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnCast$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
||||||
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
|
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
|
||||||
|
|
||||||
StringBuilder sbPlay = new StringBuilder();
|
StringBuilder sbPlay = new StringBuilder();
|
||||||
|
|||||||
@@ -984,8 +984,7 @@ public class CardProperty {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Card> cards = CardUtil.getThisTurnEntered(ZoneType.Graveyard, ZoneType.Hand, "Card", source, spellAbility);
|
if (!card.wasDiscarded()) {
|
||||||
if (!cards.contains(card) && !card.getMadnessWithoutCast()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (property.startsWith("ControlledByPlayerInTheDirection")) {
|
} else if (property.startsWith("ControlledByPlayerInTheDirection")) {
|
||||||
|
|||||||
@@ -357,6 +357,9 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
|
|
||||||
case COMBAT_END:
|
case COMBAT_END:
|
||||||
// End Combat always happens
|
// End Combat always happens
|
||||||
|
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||||
|
c.onEndOfCombat(playerTurn);
|
||||||
|
}
|
||||||
game.getEndOfCombat().executeAt();
|
game.getEndOfCombat().executeAt();
|
||||||
|
|
||||||
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
||||||
|
|||||||
@@ -1444,6 +1444,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
newCard = game.getAction().moveToGraveyard(c, sa, params);
|
newCard = game.getAction().moveToGraveyard(c, sa, params);
|
||||||
// Play the Discard sound
|
// Play the Discard sound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newCard.setDiscarded(true);
|
||||||
|
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
table.put(origin, newCard.getZone().getZoneType(), newCard);
|
table.put(origin, newCard.getZone().getZoneType(), newCard);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package forge.game.replacement;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum ReplacementLayer {
|
public enum ReplacementLayer {
|
||||||
|
CantHappen, // 614.17
|
||||||
Control, // 616.1b
|
Control, // 616.1b
|
||||||
Copy, // 616.1c
|
Copy, // 616.1c
|
||||||
Transform, // 616.1d
|
Transform, // 616.1d
|
||||||
|
|||||||
@@ -664,9 +664,9 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
// check for produce mana replacement effects - they mess this up, so just use the mana ability
|
// check for produce mana replacement effects - they mess this up, so just use the mana ability
|
||||||
final Card source = am.getHostCard();
|
final Card source = am.getHostCard();
|
||||||
final Player activator = am.getActivatingPlayer();
|
final Player activator = am.getActivatingPlayer();
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromPlayer(activator);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source);
|
||||||
repParams.put(AbilityKey.Mana, getOrigProduced());
|
repParams.put(AbilityKey.Mana, getOrigProduced());
|
||||||
repParams.put(AbilityKey.Affected, source);
|
repParams.put(AbilityKey.Activator, activator);
|
||||||
repParams.put(AbilityKey.AbilityMana, am.getRootAbility());
|
repParams.put(AbilityKey.AbilityMana, am.getRootAbility());
|
||||||
|
|
||||||
if (!source.getGame().getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other).isEmpty()) {
|
if (!source.getGame().getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other).isEmpty()) {
|
||||||
|
|||||||
@@ -1254,10 +1254,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tr = getTargetRestrictions();
|
final SpellAbility rootAbility = this.getRootAbility();
|
||||||
|
// 115.5. A spell or ability on the stack is an illegal target for itself.
|
||||||
|
// (This covers the spell case.)
|
||||||
|
if (rootAbility.isSpell() && rootAbility.getHostCard() == entity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Restriction related to this ability
|
// Restriction related to this ability
|
||||||
if (usesTargeting()) {
|
if (usesTargeting()) {
|
||||||
|
final TargetRestrictions tr = getTargetRestrictions();
|
||||||
if (tr.isUniqueTargets() && getUniqueTargets().contains(entity))
|
if (tr.isUniqueTargets() && getUniqueTargets().contains(entity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -475,12 +475,15 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.isPwAbility()) {
|
if (sa.isPwAbility()) {
|
||||||
final int initialLimit = StaticAbilityNumLoyaltyAct.limitIncrease(c) ? 1 : 0;
|
|
||||||
final int limit = StaticAbilityNumLoyaltyAct.additionalActivations(c, sa) + initialLimit;
|
|
||||||
|
|
||||||
int numActivates = c.getPlaneswalkerAbilityActivated();
|
int numActivates = c.getPlaneswalkerAbilityActivated();
|
||||||
if (numActivates > limit) {
|
int limit = StaticAbilityNumLoyaltyAct.limitIncrease(c) ? 2 : 1;
|
||||||
return false;
|
|
||||||
|
if (numActivates >= limit) {
|
||||||
|
// increased limit only counts if it's been used already
|
||||||
|
limit += StaticAbilityNumLoyaltyAct.additionalActivations(c, sa) - (limit == 1 || c.planeswalkerActivationLimitUsed() ? 0 : 1);
|
||||||
|
if (numActivates >= limit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -563,6 +563,11 @@ public final class StaticAbilityContinuous {
|
|||||||
p.setMaxHandSize(max);
|
p.setMaxHandSize(max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (params.containsKey("RaiseMaxHandSize")) {
|
||||||
|
String rmhs = params.get("RaiseMaxHandSize");
|
||||||
|
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
|
||||||
|
p.setMaxHandSize(p.getMaxHandSize() + rmax);
|
||||||
|
}
|
||||||
|
|
||||||
if (params.containsKey("AdjustLandPlays")) {
|
if (params.containsKey("AdjustLandPlays")) {
|
||||||
String mhs = params.get("AdjustLandPlays");
|
String mhs = params.get("AdjustLandPlays");
|
||||||
@@ -573,6 +578,7 @@ public final class StaticAbilityContinuous {
|
|||||||
p.addMaxLandPlays(se.getTimestamp(), add);
|
p.addMaxLandPlays(se.getTimestamp(), add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ControlOpponentsSearchingLibrary")) {
|
if (params.containsKey("ControlOpponentsSearchingLibrary")) {
|
||||||
Player cntl = Iterables.getFirst(AbilityUtils.getDefinedPlayers(hostCard, params.get("ControlOpponentsSearchingLibrary"), stAb), null);
|
Player cntl = Iterables.getFirst(AbilityUtils.getDefinedPlayers(hostCard, params.get("ControlOpponentsSearchingLibrary"), stAb), null);
|
||||||
p.addControlledWhileSearching(se.getTimestamp(), cntl);
|
p.addControlledWhileSearching(se.getTimestamp(), cntl);
|
||||||
@@ -592,12 +598,6 @@ public final class StaticAbilityContinuous {
|
|||||||
p.addAdditionalOptionalVote(se.getTimestamp(), add);
|
p.addAdditionalOptionalVote(se.getTimestamp(), add);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("RaiseMaxHandSize")) {
|
|
||||||
String rmhs = params.get("RaiseMaxHandSize");
|
|
||||||
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
|
|
||||||
p.setMaxHandSize(p.getMaxHandSize() + rmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.containsKey("ManaConversion")) {
|
if (params.containsKey("ManaConversion")) {
|
||||||
AbilityUtils.applyManaColorConversion(p.getManaPool(), params);
|
AbilityUtils.applyManaColorConversion(p.getManaPool(), params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ public class StaticAbilityPanharmonicon {
|
|||||||
}
|
}
|
||||||
} else if (trigMode.equals(TriggerType.ChangesZoneAll)) {
|
} else if (trigMode.equals(TriggerType.ChangesZoneAll)) {
|
||||||
// Check if the cards have a trigger at all
|
// Check if the cards have a trigger at all
|
||||||
final String origin = stAb.getParamOrDefault("Origin", null);
|
final String origin = stAb.getParam("Origin");
|
||||||
final String destination = stAb.getParamOrDefault("Destination", null);
|
final String destination = stAb.getParam("Destination");
|
||||||
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||||
|
|
||||||
if (table.filterCards(origin == null ? null : ImmutableList.of(ZoneType.smartValueOf(origin)), ZoneType.smartValueOf(destination), stAb.getParam("ValidCause"), card, stAb).isEmpty()) {
|
if (table.filterCards(origin == null ? null : ImmutableList.of(ZoneType.smartValueOf(origin)), ZoneType.smartValueOf(destination), stAb.getParam("ValidCause"), card, stAb).isEmpty()) {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package forge.game.trigger;
|
package forge.game.trigger;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
|
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
@@ -79,11 +77,11 @@ public class TriggerDamageDoneOnce extends Trigger {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Card> getDamageSources(Map<Card, Integer> damageMap) {
|
public CardCollection getDamageSources(Map<Card, Integer> damageMap) {
|
||||||
if (!hasParam("ValidSource")) {
|
if (!hasParam("ValidSource")) {
|
||||||
return Sets.newHashSet(damageMap.keySet());
|
return new CardCollection(damageMap.keySet());
|
||||||
}
|
}
|
||||||
Set<Card> result = Sets.newHashSet();
|
CardCollection result = new CardCollection();
|
||||||
for (Card c : damageMap.keySet()) {
|
for (Card c : damageMap.keySet()) {
|
||||||
if (matchesValid(c, getParam("ValidSource").split(","))) {
|
if (matchesValid(c, getParam("ValidSource").split(","))) {
|
||||||
result.add(c);
|
result.add(c);
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
|
|
||||||
if (sp.getApi() == ApiType.Charm && sp.hasParam("ChoiceRestriction")) {
|
if (sp.getApi() == ApiType.Charm && sp.hasParam("ChoiceRestriction")) {
|
||||||
// Remember the Choice here for later handling
|
// Remember the Choice here for later handling
|
||||||
source.addChosenModes(sp, sp.getSubAbility().getDescription());
|
source.addChosenModes(sp, sp.getSubAbility().getDescription(), game.getPhaseHandler().inCombat());
|
||||||
}
|
}
|
||||||
|
|
||||||
//cancel auto-pass for all opponents of activating player
|
//cancel auto-pass for all opponents of activating player
|
||||||
@@ -857,7 +857,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
final Trigger trig = sa.getTrigger();
|
final Trigger trig = sa.getTrigger();
|
||||||
final Card newHost = game.getCardState(host);
|
final Card newHost = game.getCardState(host);
|
||||||
if (host.isAura() && newHost.isInZone(ZoneType.Graveyard) && trig.getMode() == TriggerType.ChangesZone &&
|
if (host.isAura() && newHost.isInZone(ZoneType.Graveyard) && trig.getMode() == TriggerType.ChangesZone &&
|
||||||
trig.getParam("Destination").equals("Graveyard") && trig.getParam("ValidCard").equals("Card.EnchantedBy")) {
|
"Graveyard".equals(trig.getParam("Destination")) && "Card.EnchantedBy".equals(trig.getParam("ValidCard"))) {
|
||||||
sa.setHostCard(newHost);
|
sa.setHostCard(newHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package forge.game.zone;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.card.CardStateName;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
@@ -63,7 +64,7 @@ public class PlayerZone extends Zone {
|
|||||||
boolean graveyardCastable = c.hasKeyword(Keyword.FLASHBACK) ||
|
boolean graveyardCastable = c.hasKeyword(Keyword.FLASHBACK) ||
|
||||||
c.hasKeyword(Keyword.RETRACE) || c.hasKeyword(Keyword.JUMP_START) || c.hasKeyword(Keyword.ESCAPE) ||
|
c.hasKeyword(Keyword.RETRACE) || c.hasKeyword(Keyword.JUMP_START) || c.hasKeyword(Keyword.ESCAPE) ||
|
||||||
c.hasKeyword(Keyword.DISTURB);
|
c.hasKeyword(Keyword.DISTURB);
|
||||||
boolean exileCastable = (c.isAdventureCard() || c.isForetold()) && c.isInZone(ZoneType.Exile);
|
boolean exileCastable = c.isForetold() || isOnAdventure(c);
|
||||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||||
final ZoneType restrictZone = sa.getRestrictions().getZone();
|
final ZoneType restrictZone = sa.getRestrictions().getZone();
|
||||||
|
|
||||||
@@ -92,6 +93,15 @@ public class PlayerZone extends Zone {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private boolean isOnAdventure(Card c) {
|
||||||
|
if (!c.isAdventureCard())
|
||||||
|
return false;
|
||||||
|
if (c.getExiledWith() == null)
|
||||||
|
return false;
|
||||||
|
if (!CardStateName.Adventure.equals(c.getExiledWith().getCurrentStateName()))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
|
|||||||
@@ -65,16 +65,6 @@ public class PlayerZoneBattlefield extends PlayerZone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public final void remove(final Card c) {
|
|
||||||
super.remove(c);
|
|
||||||
|
|
||||||
if (trigger) {
|
|
||||||
c.runLeavesPlayCommands();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setTriggers(final boolean b) {
|
public final void setTriggers(final boolean b) {
|
||||||
trigger = b;
|
trigger = b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class MessageUtil {
|
|||||||
String choser = StringUtils.capitalize(mayBeYou(player, target));
|
String choser = StringUtils.capitalize(mayBeYou(player, target));
|
||||||
switch(sa.getApi()) {
|
switch(sa.getApi()) {
|
||||||
case ChooseDirection:
|
case ChooseDirection:
|
||||||
|
case DigMultiple:
|
||||||
return value;
|
return value;
|
||||||
case ChooseColor:
|
case ChooseColor:
|
||||||
return sa.hasParam("Random")
|
return sa.hasParam("Random")
|
||||||
|
|||||||
@@ -840,9 +840,9 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
|||||||
try {
|
try {
|
||||||
if (StringUtils.isBlank(savedState)) {
|
if (StringUtils.isBlank(savedState)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
} else {
|
|
||||||
return Arrays.asList(savedState.split(";")[1].split(SELECTED_DECK_DELIMITER));
|
|
||||||
}
|
}
|
||||||
|
final String[] parts = savedState.split(";", -1);
|
||||||
|
return Arrays.asList(parts[1].split(SELECTED_DECK_DELIMITER));
|
||||||
} catch (final Exception ex) {
|
} catch (final Exception ex) {
|
||||||
System.err.println(ex + " [savedState=" + savedState + "]");
|
System.err.println(ex + " [savedState=" + savedState + "]");
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ public class FDeckViewer extends FDialog {
|
|||||||
sectionCards = new TreeMap<>();
|
sectionCards = new TreeMap<>();
|
||||||
deckList.append(nl);
|
deckList.append(nl);
|
||||||
for (final Entry<PaperCard, Integer> ev : cp) {
|
for (final Entry<PaperCard, Integer> ev : cp) {
|
||||||
cardName = ev.getKey().toString();
|
cardName = ev.getKey().getCardName();
|
||||||
if (sectionCards.containsKey(cardName)) {
|
if (sectionCards.containsKey(cardName)) {
|
||||||
sectionCards.put(cardName, (int)sectionCards.get(cardName) + ev.getValue());
|
sectionCards.put(cardName, (int)sectionCards.get(cardName) + ev.getValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import forge.item.PaperCard;
|
|||||||
import forge.itemmanager.ItemManager;
|
import forge.itemmanager.ItemManager;
|
||||||
import forge.screens.home.quest.DialogChooseSets;
|
import forge.screens.home.quest.DialogChooseSets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -26,7 +26,9 @@ public class CardSetFilter extends CardFormatFilter {
|
|||||||
|
|
||||||
public CardSetFilter(ItemManager<? super PaperCard> itemManager0, Collection<String> sets0, Collection<String> limitedSets0, boolean allowReprints0){
|
public CardSetFilter(ItemManager<? super PaperCard> itemManager0, Collection<String> sets0, Collection<String> limitedSets0, boolean allowReprints0){
|
||||||
this(itemManager0, sets0, allowReprints0);
|
this(itemManager0, sets0, allowReprints0);
|
||||||
this.limitedSets.addAll(limitedSets0);
|
if (limitedSets0 != null) {
|
||||||
|
this.limitedSets.addAll(limitedSets0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,7 +62,7 @@ public class CardSetFilter extends CardFormatFilter {
|
|||||||
final DialogChooseSets dialog = new DialogChooseSets(this.sets, null, this.limitedSets,
|
final DialogChooseSets dialog = new DialogChooseSets(this.sets, null, this.limitedSets,
|
||||||
true, this.allowReprints);
|
true, this.allowReprints);
|
||||||
final CardSetFilter itemFilter = this;
|
final CardSetFilter itemFilter = this;
|
||||||
|
|
||||||
dialog.setOkCallback(new Runnable() {
|
dialog.setOkCallback(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package forge.screens.home;
|
package forge.screens.home;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
@@ -10,8 +8,6 @@ import javax.swing.SwingUtilities;
|
|||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.deck.DeckProxy;
|
import forge.deck.DeckProxy;
|
||||||
import forge.deck.DeckType;
|
|
||||||
import forge.deckchooser.FDeckChooser;
|
|
||||||
import forge.localinstance.properties.ForgePreferences;
|
import forge.localinstance.properties.ForgePreferences;
|
||||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||||
import forge.model.FModel;
|
import forge.model.FModel;
|
||||||
@@ -22,12 +18,10 @@ public class CLobby {
|
|||||||
private final VLobby view;
|
private final VLobby view;
|
||||||
public CLobby(final VLobby view) {
|
public CLobby(final VLobby view) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.view.setForCommander(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDecks(final Iterable<DeckProxy> commanderDecks, FList<Object> deckList, String... initialItems) {
|
private void addDecks(final Iterable<DeckProxy> commanderDecks, FList<Object> deckList, String... initialItems) {
|
||||||
Vector<Object> listData = new Vector<>();
|
Vector<Object> listData = new Vector<>(Arrays.asList(initialItems));
|
||||||
listData.addAll(Arrays.asList(initialItems));
|
|
||||||
listData.add("Generate");
|
listData.add("Generate");
|
||||||
if (!Iterables.isEmpty(commanderDecks)) {
|
if (!Iterables.isEmpty(commanderDecks)) {
|
||||||
listData.add("Random");
|
listData.add("Random");
|
||||||
@@ -46,89 +40,38 @@ public class CLobby {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override public final void run() {
|
final Iterable<DeckProxy> schemeDecks = DeckProxy.getAllSchemeDecks();
|
||||||
final Iterable<DeckProxy> schemeDecks = DeckProxy.getAllSchemeDecks();
|
final Iterable<DeckProxy> planarDecks = DeckProxy.getAllPlanarDecks();
|
||||||
final Iterable<DeckProxy> planarDecks = DeckProxy.getAllPlanarDecks();
|
|
||||||
|
|
||||||
for (int i = 0; i < VLobby.MAX_PLAYERS; i++) {
|
for (int i = 0; i < VLobby.MAX_PLAYERS; i++) {
|
||||||
addDecks(schemeDecks, view.getSchemeDeckLists().get(i),
|
addDecks(schemeDecks, view.getSchemeDeckLists().get(i),
|
||||||
"Use deck's scheme section (random if unavailable)");
|
"Use deck's scheme section (random if unavailable)");
|
||||||
addDecks(planarDecks, view.getPlanarDeckLists().get(i),
|
addDecks(planarDecks, view.getPlanarDeckLists().get(i),
|
||||||
"Use deck's planes section (random if unavailable)");
|
"Use deck's planes section (random if unavailable)");
|
||||||
view.updateVanguardList(i);
|
view.updateVanguardList(i);
|
||||||
}
|
|
||||||
|
|
||||||
// General updates when switching back to this view
|
|
||||||
view.getBtnStart().requestFocusInWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General updates when switching back to this view
|
||||||
|
view.getBtnStart().requestFocusInWindow();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
for (int iSlot = 0; iSlot < VLobby.MAX_PLAYERS; iSlot++) {
|
|
||||||
final FDeckChooser fdc = view.getDeckChooser(iSlot);
|
|
||||||
fdc.initialize(FPref.CONSTRUCTED_DECK_STATES[iSlot], defaultDeckTypeForSlot(iSlot));
|
|
||||||
fdc.populate();
|
|
||||||
/*fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() {
|
|
||||||
@Override public final void deckTypeSelected(final DecksComboBoxEvent ev) {
|
|
||||||
view.focusOnAvatar();
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
final FDeckChooser fdccom = view.getCommanderDeckChooser(iSlot);
|
|
||||||
fdccom.initialize(FPref.COMMANDER_DECK_STATES[iSlot], defaultDeckTypeForCommanderSlot(iSlot));
|
|
||||||
fdccom.populate();
|
|
||||||
final FDeckChooser fdobcom = view.getOathbreakerDeckChooser(iSlot);
|
|
||||||
fdobcom.initialize(FPref.OATHBREAKER_DECK_STATES[iSlot], defaultDeckTypeForOathbreakerSlot(iSlot));
|
|
||||||
fdobcom.populate();
|
|
||||||
final FDeckChooser fdtlcom = view.getTinyLeaderDeckChooser(iSlot);
|
|
||||||
fdtlcom.initialize(FPref.TINY_LEADER_DECK_STATES[iSlot], defaultDeckTypeForTinyLeaderSlot(iSlot));
|
|
||||||
fdtlcom.populate();
|
|
||||||
final FDeckChooser fdbcom = view.getBrawlDeckChooser(iSlot);
|
|
||||||
fdbcom.initialize(FPref.BRAWL_DECK_STATES[iSlot], defaultDeckTypeForBrawlSlot(iSlot));
|
|
||||||
fdbcom.populate();
|
|
||||||
}
|
|
||||||
|
|
||||||
final ForgePreferences prefs = FModel.getPreferences();
|
final ForgePreferences prefs = FModel.getPreferences();
|
||||||
// Checkbox event handling
|
// Checkbox event handling
|
||||||
view.getCbSingletons().addActionListener(new ActionListener() {
|
view.getCbSingletons().addActionListener(arg0 -> {
|
||||||
@Override
|
prefs.setPref(FPref.DECKGEN_SINGLETONS, String.valueOf(view.getCbSingletons().isSelected()));
|
||||||
public void actionPerformed(final ActionEvent arg0) {
|
prefs.save();
|
||||||
prefs.setPref(FPref.DECKGEN_SINGLETONS, String.valueOf(view.getCbSingletons().isSelected()));
|
|
||||||
prefs.save();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
view.getCbArtifacts().addActionListener(new ActionListener() {
|
view.getCbArtifacts().addActionListener(arg0 -> {
|
||||||
@Override
|
prefs.setPref(FPref.DECKGEN_ARTIFACTS, String.valueOf(view.getCbArtifacts().isSelected()));
|
||||||
public void actionPerformed(final ActionEvent arg0) {
|
prefs.save();
|
||||||
prefs.setPref(FPref.DECKGEN_ARTIFACTS, String.valueOf(view.getCbArtifacts().isSelected()));
|
|
||||||
prefs.save();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pre-select checkboxes
|
// Pre-select checkboxes
|
||||||
view.getCbSingletons().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
view.getCbSingletons().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS));
|
||||||
view.getCbArtifacts().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
view.getCbArtifacts().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DeckType defaultDeckTypeForSlot(final int iSlot) {
|
|
||||||
return iSlot == 0 ? DeckType.PRECONSTRUCTED_DECK : DeckType.COLOR_DECK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DeckType defaultDeckTypeForCommanderSlot(final int iSlot) {
|
|
||||||
return iSlot == 0 ? DeckType.COMMANDER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DeckType defaultDeckTypeForOathbreakerSlot(final int iSlot) {
|
|
||||||
return iSlot == 0 ? DeckType.OATHBREAKER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DeckType defaultDeckTypeForTinyLeaderSlot(final int iSlot) {
|
|
||||||
return iSlot == 0 ? DeckType.TINY_LEADERS_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DeckType defaultDeckTypeForBrawlSlot(final int iSlot) {
|
|
||||||
return iSlot == 0 ? DeckType.BRAWL_DECK : DeckType.CUSTOM_DECK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.screens.home;
|
package forge.screens.home;
|
||||||
|
|
||||||
|
import forge.deckchooser.FDeckChooser;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@@ -103,6 +104,8 @@ public class PlayerPanel extends FPanel {
|
|||||||
|
|
||||||
private boolean allowNetworking;
|
private boolean allowNetworking;
|
||||||
|
|
||||||
|
private FDeckChooser deckChooser;
|
||||||
|
|
||||||
private final VLobby lobby;
|
private final VLobby lobby;
|
||||||
public PlayerPanel(final VLobby lobby, final boolean allowNetworking, final int index, final LobbySlot slot, final boolean mayEdit, final boolean mayControl) {
|
public PlayerPanel(final VLobby lobby, final boolean allowNetworking, final int index, final LobbySlot slot, final boolean mayEdit, final boolean mayControl) {
|
||||||
super();
|
super();
|
||||||
@@ -463,10 +466,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
radioAiUseSimulation.setSelected(useSimulation);
|
radioAiUseSimulation.setSelected(useSimulation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLocal() {
|
|
||||||
return type == LobbySlotType.LOCAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isArchenemy() {
|
public boolean isArchenemy() {
|
||||||
return aeTeamComboBox.getSelectedIndex() == 0;
|
return aeTeamComboBox.getSelectedIndex() == 0;
|
||||||
}
|
}
|
||||||
@@ -540,9 +539,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
private void addHandlersToVariantsControls() {
|
private void addHandlersToVariantsControls() {
|
||||||
// Archenemy buttons
|
// Archenemy buttons
|
||||||
scmDeckSelectorBtn.setCommand(new Runnable() {
|
scmDeckSelectorBtn.setCommand(new Runnable() {
|
||||||
@@ -621,9 +617,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
private void createPlayerTypeOptions() {
|
private void createPlayerTypeOptions() {
|
||||||
radioHuman = new FRadioButton(localizer.getMessage("lblHuman"));
|
radioHuman = new FRadioButton(localizer.getMessage("lblHuman"));
|
||||||
radioAi = new FRadioButton(localizer.getMessage("lblAI"));
|
radioAi = new FRadioButton(localizer.getMessage("lblAI"));
|
||||||
@@ -674,9 +667,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
private void addHandlersDeckSelector() {
|
private void addHandlersDeckSelector() {
|
||||||
deckBtn.setCommand(new Runnable() {
|
deckBtn.setCommand(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -688,10 +678,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private FLabel createNameRandomizer() {
|
private FLabel createNameRandomizer() {
|
||||||
final FLabel newNameBtn = new FLabel.Builder().tooltip(localizer.getMessage("lblGetaNewRandomName")).iconInBackground(false)
|
final FLabel newNameBtn = new FLabel.Builder().tooltip(localizer.getMessage("lblGetaNewRandomName")).iconInBackground(false)
|
||||||
.icon(FSkin.getIcon(FSkinProp.ICO_EDIT)).hoverable(true).opaque(false)
|
.icon(FSkin.getIcon(FSkinProp.ICO_EDIT)).hoverable(true).opaque(false)
|
||||||
@@ -717,10 +703,6 @@ public class PlayerPanel extends FPanel {
|
|||||||
return newNameBtn;
|
return newNameBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private void createNameEditor() {
|
private void createNameEditor() {
|
||||||
String name;
|
String name;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
@@ -898,4 +880,12 @@ public class PlayerPanel extends FPanel {
|
|||||||
public void setMayRemove(final boolean mayRemove) {
|
public void setMayRemove(final boolean mayRemove) {
|
||||||
this.mayRemove = mayRemove;
|
this.mayRemove = mayRemove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FDeckChooser getDeckChooser() {
|
||||||
|
return deckChooser;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeckChooser(final FDeckChooser deckChooser) {
|
||||||
|
this.deckChooser = deckChooser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,11 @@ import java.awt.Font;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
import java.awt.event.ItemListener;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -36,7 +36,6 @@ import forge.gamemodes.match.LobbySlotType;
|
|||||||
import forge.gamemodes.net.event.UpdateLobbyPlayerEvent;
|
import forge.gamemodes.net.event.UpdateLobbyPlayerEvent;
|
||||||
import forge.gui.CardDetailPanel;
|
import forge.gui.CardDetailPanel;
|
||||||
import forge.gui.GuiBase;
|
import forge.gui.GuiBase;
|
||||||
import forge.gui.UiCommand;
|
|
||||||
import forge.gui.interfaces.ILobbyView;
|
import forge.gui.interfaces.ILobbyView;
|
||||||
import forge.gui.util.SOptionPane;
|
import forge.gui.util.SOptionPane;
|
||||||
import forge.interfaces.IPlayerChangeListener;
|
import forge.interfaces.IPlayerChangeListener;
|
||||||
@@ -105,22 +104,18 @@ public class VLobby implements ILobbyView {
|
|||||||
private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3"));
|
private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3"));
|
||||||
private final FScrollPanel playersScroll = new FScrollPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"), true);
|
private final FScrollPanel playersScroll = new FScrollPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"), true);
|
||||||
private final List<PlayerPanel> playerPanels = new ArrayList<>(MAX_PLAYERS);
|
private final List<PlayerPanel> playerPanels = new ArrayList<>(MAX_PLAYERS);
|
||||||
|
// Cache deck choosers so switching settings doesn't re-generate random decks.
|
||||||
|
private final Map<FPref, FDeckChooser> cachedDeckChoosers = new HashMap<>();
|
||||||
|
|
||||||
private final FLabel addPlayerBtn = new FLabel.ButtonBuilder().fontSize(14).text(localizer.getMessage("lblAddAPlayer")).build();
|
private final FLabel addPlayerBtn = new FLabel.ButtonBuilder().fontSize(14).text(localizer.getMessage("lblAddAPlayer")).build();
|
||||||
|
|
||||||
// Deck frame elements
|
// Deck frame elements
|
||||||
private final JPanel decksFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"));
|
private final JPanel decksFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"));
|
||||||
private final List<FDeckChooser> deckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
|
||||||
private final FCheckBox cbSingletons = new FCheckBox(localizer.getMessage("cbSingletons"));
|
private final FCheckBox cbSingletons = new FCheckBox(localizer.getMessage("cbSingletons"));
|
||||||
private final FCheckBox cbArtifacts = new FCheckBox(localizer.getMessage("cbRemoveArtifacts"));
|
private final FCheckBox cbArtifacts = new FCheckBox(localizer.getMessage("cbRemoveArtifacts"));
|
||||||
private final Deck[] decks = new Deck[MAX_PLAYERS];
|
private final Deck[] decks = new Deck[MAX_PLAYERS];
|
||||||
|
|
||||||
// Variants
|
// Variants
|
||||||
private final List<FDeckChooser> commanderDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
|
||||||
private final List<FDeckChooser> oathbreakerDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
|
||||||
private final List<FDeckChooser> tinyLeadersDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
|
||||||
private final List<FDeckChooser> brawlDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS);
|
|
||||||
|
|
||||||
private final List<FList<Object>> schemeDeckLists = new ArrayList<>();
|
private final List<FList<Object>> schemeDeckLists = new ArrayList<>();
|
||||||
private final List<FPanel> schemeDeckPanels = new ArrayList<>(MAX_PLAYERS);
|
private final List<FPanel> schemeDeckPanels = new ArrayList<>(MAX_PLAYERS);
|
||||||
|
|
||||||
@@ -131,22 +126,11 @@ public class VLobby implements ILobbyView {
|
|||||||
private final List<FPanel> vgdPanels = new ArrayList<>(MAX_PLAYERS);
|
private final List<FPanel> vgdPanels = new ArrayList<>(MAX_PLAYERS);
|
||||||
private final List<CardDetailPanel> vgdAvatarDetails = new ArrayList<>();
|
private final List<CardDetailPanel> vgdAvatarDetails = new ArrayList<>();
|
||||||
private final List<PaperCard> vgdAllAvatars = new ArrayList<>();
|
private final List<PaperCard> vgdAllAvatars = new ArrayList<>();
|
||||||
private final List<PaperCard> vgdAllAiAvatars = new ArrayList<>();
|
|
||||||
private final List<PaperCard> nonRandomHumanAvatars = new ArrayList<>();
|
private final List<PaperCard> nonRandomHumanAvatars = new ArrayList<>();
|
||||||
private final List<PaperCard> nonRandomAiAvatars = new ArrayList<>();
|
private final List<PaperCard> nonRandomAiAvatars = new ArrayList<>();
|
||||||
private final Vector<Object> humanListData = new Vector<>();
|
private final Vector<Object> humanListData = new Vector<>();
|
||||||
private final Vector<Object> aiListData = new Vector<>();
|
private final Vector<Object> aiListData = new Vector<>();
|
||||||
|
|
||||||
public boolean isForCommander() {
|
|
||||||
return isForCommander;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setForCommander(boolean forCommander) {
|
|
||||||
isForCommander = forCommander;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isForCommander = false;
|
|
||||||
|
|
||||||
// CTR
|
// CTR
|
||||||
public VLobby(final GameLobby lobby) {
|
public VLobby(final GameLobby lobby) {
|
||||||
this.lobby = lobby;
|
this.lobby = lobby;
|
||||||
@@ -178,12 +162,7 @@ public class VLobby implements ILobbyView {
|
|||||||
|
|
||||||
if (lobby.hasControl()) {
|
if (lobby.hasControl()) {
|
||||||
addPlayerBtn.setFocusable(true);
|
addPlayerBtn.setFocusable(true);
|
||||||
addPlayerBtn.setCommand(new Runnable() {
|
addPlayerBtn.setCommand(lobby::addSlot);
|
||||||
@Override
|
|
||||||
public final void run() {
|
|
||||||
lobby.addSlot();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx");
|
playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +186,7 @@ public class VLobby implements ILobbyView {
|
|||||||
// Start button event handling
|
// Start button event handling
|
||||||
btnStart.addActionListener(new ActionListener() {
|
btnStart.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public final void actionPerformed(final ActionEvent arg0) {
|
public void actionPerformed(final ActionEvent arg0) {
|
||||||
Runnable startGame = lobby.startGame();
|
Runnable startGame = lobby.startGame();
|
||||||
if (startGame != null) {
|
if (startGame != null) {
|
||||||
if (!gamesInMatch.getSelectedItem().equals(ForgePreferences.FPref.UI_MATCHES_PER_GAME)) {
|
if (!gamesInMatch.getSelectedItem().equals(ForgePreferences.FPref.UI_MATCHES_PER_GAME)) {
|
||||||
@@ -226,17 +205,8 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateDeckPanel() {
|
public void updateDeckPanel() {
|
||||||
for (int iPlayer = 0; iPlayer < activePlayersNum; iPlayer++) {
|
for (final PlayerPanel playerPanel : playerPanels) {
|
||||||
final FDeckChooser fdc = getDeckChooser(iPlayer);
|
playerPanel.getDeckChooser().restoreSavedState();
|
||||||
fdc.restoreSavedState();
|
|
||||||
final FDeckChooser fdcom = getCommanderDeckChooser(iPlayer);
|
|
||||||
fdcom.restoreSavedState();
|
|
||||||
final FDeckChooser fdob = getOathbreakerDeckChooser(iPlayer);
|
|
||||||
fdob.restoreSavedState();
|
|
||||||
final FDeckChooser fdtl = getTinyLeaderDeckChooser(iPlayer);
|
|
||||||
fdtl.restoreSavedState();
|
|
||||||
final FDeckChooser fdbr = getBrawlDeckChooser(iPlayer);
|
|
||||||
fdbr.restoreSavedState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +214,12 @@ public class VLobby implements ILobbyView {
|
|||||||
getPlayerPanelWithFocus().focusOnAvatar();
|
getPlayerPanelWithFocus().focusOnAvatar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PlayerPanel getPlayerPanel(int slot) {
|
||||||
|
return playerPanels.get(slot);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(final int slot, final LobbySlotType type){
|
public void update(final int slot, final LobbySlotType type) {
|
||||||
final FDeckChooser deckChooser = getDeckChooser(slot);
|
final FDeckChooser deckChooser = getDeckChooser(slot);
|
||||||
deckChooser.setIsAi(type==LobbySlotType.AI);
|
deckChooser.setIsAi(type==LobbySlotType.AI);
|
||||||
DeckType selectedDeckType = deckChooser.getSelectedDeckType();
|
DeckType selectedDeckType = deckChooser.getSelectedDeckType();
|
||||||
@@ -259,28 +233,13 @@ public class VLobby implements ILobbyView {
|
|||||||
case COLOR_DECK:
|
case COLOR_DECK:
|
||||||
case STANDARD_COLOR_DECK:
|
case STANDARD_COLOR_DECK:
|
||||||
case MODERN_COLOR_DECK:
|
case MODERN_COLOR_DECK:
|
||||||
|
case RANDOM_CARDGEN_COMMANDER_DECK:
|
||||||
|
case RANDOM_COMMANDER_DECK:
|
||||||
deckChooser.refreshDeckListForAI();
|
deckChooser.refreshDeckListForAI();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
updateCommanderStyleDeckChooser(getCommanderDeckChooser(slot), type);
|
|
||||||
updateCommanderStyleDeckChooser(getOathbreakerDeckChooser(slot), type);
|
|
||||||
updateCommanderStyleDeckChooser(getTinyLeaderDeckChooser(slot), type);
|
|
||||||
updateCommanderStyleDeckChooser(getBrawlDeckChooser(slot), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCommanderStyleDeckChooser(final FDeckChooser deckChooser, final LobbySlotType type) {
|
|
||||||
deckChooser.setIsAi(type==LobbySlotType.AI);
|
|
||||||
DeckType selectedDeckType = deckChooser.getSelectedDeckType();
|
|
||||||
switch (selectedDeckType){
|
|
||||||
case RANDOM_CARDGEN_COMMANDER_DECK:
|
|
||||||
case RANDOM_COMMANDER_DECK:
|
|
||||||
deckChooser.refreshDeckListForAI();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -308,11 +267,6 @@ public class VLobby implements ILobbyView {
|
|||||||
if (i < activePlayersNum) {
|
if (i < activePlayersNum) {
|
||||||
// visible panels
|
// visible panels
|
||||||
final LobbySlot slot = lobby.getSlot(i);
|
final LobbySlot slot = lobby.getSlot(i);
|
||||||
final FDeckChooser deckChooser = getDeckChooser(i);
|
|
||||||
final FDeckChooser commanderDeckChooser = getCommanderDeckChooser(i);
|
|
||||||
final FDeckChooser oathbreakerDeckChooser = getOathbreakerDeckChooser(i);
|
|
||||||
final FDeckChooser tinyLeaderDeckChooser = getTinyLeaderDeckChooser(i);
|
|
||||||
final FDeckChooser brawlDeckChooser = getBrawlDeckChooser(i);
|
|
||||||
final PlayerPanel panel;
|
final PlayerPanel panel;
|
||||||
final boolean isNewPanel;
|
final boolean isNewPanel;
|
||||||
if (hasPanel) {
|
if (hasPanel) {
|
||||||
@@ -326,15 +280,6 @@ public class VLobby implements ILobbyView {
|
|||||||
constraints += ", gaptop 5px";
|
constraints += ", gaptop 5px";
|
||||||
}
|
}
|
||||||
playersScroll.add(panel, constraints);
|
playersScroll.add(panel, constraints);
|
||||||
deckChooser.restoreSavedState();
|
|
||||||
commanderDeckChooser.restoreSavedState();
|
|
||||||
oathbreakerDeckChooser.restoreSavedState();
|
|
||||||
tinyLeaderDeckChooser.restoreSavedState();
|
|
||||||
brawlDeckChooser.restoreSavedState();
|
|
||||||
if (i == 0) {
|
|
||||||
slot.setIsDevMode(prefs.getPrefBoolean(FPref.DEV_MODE_ENABLED));
|
|
||||||
changePlayerFocus(0);
|
|
||||||
}
|
|
||||||
isNewPanel = true;
|
isNewPanel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +298,24 @@ public class VLobby implements ILobbyView {
|
|||||||
panel.update();
|
panel.update();
|
||||||
|
|
||||||
final boolean isSlotAI = slot.getType() == LobbySlotType.AI;
|
final boolean isSlotAI = slot.getType() == LobbySlotType.AI;
|
||||||
|
if (isNewPanel || fullUpdate) {
|
||||||
deckChooser.setIsAi(isSlotAI);
|
final FDeckChooser deckChooser = createDeckChooser(lobby.getGameType(), i, isSlotAI);
|
||||||
commanderDeckChooser.setIsAi(isSlotAI);
|
deckChooser.populate();
|
||||||
oathbreakerDeckChooser.setIsAi(isSlotAI);
|
panel.setDeckChooser(deckChooser);
|
||||||
tinyLeaderDeckChooser.setIsAi(isSlotAI);
|
if (i == 0) {
|
||||||
brawlDeckChooser.setIsAi(isSlotAI);
|
// TODO: This seems like the wrong place to do this:
|
||||||
|
slot.setIsDevMode(prefs.getPrefBoolean(FPref.DEV_MODE_ENABLED));
|
||||||
|
changePlayerFocus(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panel.getDeckChooser().setIsAi(isSlotAI);
|
||||||
|
}
|
||||||
if (fullUpdate && (type == LobbySlotType.LOCAL || isSlotAI)) {
|
if (fullUpdate && (type == LobbySlotType.LOCAL || isSlotAI)) {
|
||||||
selectDeck(i);
|
// Deck section selection
|
||||||
|
panel.getDeckChooser().getLstDecks().getSelectCommand().run();
|
||||||
|
selectSchemeDeck(i);
|
||||||
|
selectPlanarDeck(i);
|
||||||
|
selectVanguardAvatar(i);
|
||||||
}
|
}
|
||||||
if (isNewPanel) {
|
if (isNewPanel) {
|
||||||
panel.setVisible(true);
|
panel.setVisible(true);
|
||||||
@@ -373,7 +328,7 @@ public class VLobby implements ILobbyView {
|
|||||||
if (playerWithFocus >= activePlayersNum) {
|
if (playerWithFocus >= activePlayersNum) {
|
||||||
changePlayerFocus(activePlayersNum - 1);
|
changePlayerFocus(activePlayersNum - 1);
|
||||||
} else {
|
} else {
|
||||||
populateDeckPanel(getCurrentGameMode());
|
populateDeckPanel(lobby.getGameType());
|
||||||
}
|
}
|
||||||
refreshPanels(true, true);
|
refreshPanels(true, true);
|
||||||
}
|
}
|
||||||
@@ -395,8 +350,7 @@ public class VLobby implements ILobbyView {
|
|||||||
void setDevMode(final int index) {
|
void setDevMode(final int index) {
|
||||||
// clear ready for everyone
|
// clear ready for everyone
|
||||||
for (int i = 0; i < activePlayersNum; i++) {
|
for (int i = 0; i < activePlayersNum; i++) {
|
||||||
final PlayerPanel panel = playerPanels.get(i);
|
getPlayerPanel(i).setIsReady(false);
|
||||||
panel.setIsReady(false);
|
|
||||||
firePlayerChangeListener(i);
|
firePlayerChangeListener(i);
|
||||||
}
|
}
|
||||||
changePlayerFocus(index);
|
changePlayerFocus(index);
|
||||||
@@ -430,7 +384,7 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private UpdateLobbyPlayerEvent getSlot(final int index) {
|
private UpdateLobbyPlayerEvent getSlot(final int index) {
|
||||||
final PlayerPanel panel = playerPanels.get(index);
|
final PlayerPanel panel = getPlayerPanel(index);
|
||||||
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), -1/*TODO panel.getSleeveIndex()*/, panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), -1/*TODO panel.getSleeveIndex()*/, panel.getTeam(), panel.isArchenemy(), panel.isReady(), panel.isDevMode(), panel.getAiOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,16 +392,6 @@ public class VLobby implements ILobbyView {
|
|||||||
* These are added to a list which can be referenced to populate the deck panel appropriately. */
|
* These are added to a list which can be referenced to populate the deck panel appropriately. */
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private void buildDeckPanels(final int playerIndex) {
|
private void buildDeckPanels(final int playerIndex) {
|
||||||
// Main deck
|
|
||||||
final FDeckChooser mainChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Constructed, false);
|
|
||||||
mainChooser.getLstDecks().setSelectCommand(new UiCommand() {
|
|
||||||
@Override public final void run() {
|
|
||||||
selectMainDeck(playerIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mainChooser.initialize();
|
|
||||||
deckChoosers.add(mainChooser);
|
|
||||||
|
|
||||||
// Scheme deck list
|
// Scheme deck list
|
||||||
buildDeckPanel(localizer.getMessage("lblSchemeDeck"), playerIndex, schemeDeckLists, schemeDeckPanels, new ListSelectionListener() {
|
buildDeckPanel(localizer.getMessage("lblSchemeDeck"), playerIndex, schemeDeckLists, schemeDeckPanels, new ListSelectionListener() {
|
||||||
@Override public final void valueChanged(final ListSelectionEvent e) {
|
@Override public final void valueChanged(final ListSelectionEvent e) {
|
||||||
@@ -455,42 +399,6 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final FDeckChooser commanderChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Commander, true);
|
|
||||||
commanderChooser.getLstDecks().setSelectCommand(new UiCommand() {
|
|
||||||
@Override public final void run() {
|
|
||||||
selectCommanderDeck(playerIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
commanderChooser.initialize();
|
|
||||||
commanderDeckChoosers.add(commanderChooser);
|
|
||||||
|
|
||||||
final FDeckChooser oathbreakerChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Oathbreaker, true);
|
|
||||||
oathbreakerChooser.getLstDecks().setSelectCommand(new UiCommand() {
|
|
||||||
@Override public final void run() {
|
|
||||||
selectOathbreakerDeck(playerIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
oathbreakerChooser.initialize();
|
|
||||||
oathbreakerDeckChoosers.add(oathbreakerChooser);
|
|
||||||
|
|
||||||
final FDeckChooser tinyLeaderChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.TinyLeaders, true);
|
|
||||||
tinyLeaderChooser.getLstDecks().setSelectCommand(new UiCommand() {
|
|
||||||
@Override public final void run() {
|
|
||||||
selectTinyLeadersDeck(playerIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tinyLeaderChooser.initialize();
|
|
||||||
tinyLeadersDeckChoosers.add(tinyLeaderChooser);
|
|
||||||
|
|
||||||
final FDeckChooser brawlChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Brawl, true);
|
|
||||||
brawlChooser.getLstDecks().setSelectCommand(new UiCommand() {
|
|
||||||
@Override public final void run() {
|
|
||||||
selectBrawlDeck(playerIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
brawlChooser.initialize();
|
|
||||||
brawlDeckChoosers.add(brawlChooser);
|
|
||||||
|
|
||||||
// Planar deck list
|
// Planar deck list
|
||||||
buildDeckPanel(localizer.getMessage("lblPlanarDeck"), playerIndex, planarDeckLists, planarDeckPanels, new ListSelectionListener() {
|
buildDeckPanel(localizer.getMessage("lblPlanarDeck"), playerIndex, planarDeckLists, planarDeckPanels, new ListSelectionListener() {
|
||||||
@Override public final void valueChanged(final ListSelectionEvent e) {
|
@Override public final void valueChanged(final ListSelectionEvent e) {
|
||||||
@@ -532,26 +440,11 @@ public class VLobby implements ILobbyView {
|
|||||||
deckPanels.add(deckPanel);
|
deckPanels.add(deckPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectDeck(final int playerIndex) {
|
private FDeckChooser getDeckChooser(final int iSlot) {
|
||||||
// Full deck selection
|
return getPlayerPanel(iSlot).getDeckChooser();
|
||||||
selectMainDeck(playerIndex);
|
|
||||||
selectCommanderDeck(playerIndex);
|
|
||||||
selectOathbreakerDeck(playerIndex);
|
|
||||||
selectTinyLeadersDeck(playerIndex);
|
|
||||||
selectBrawlDeck(playerIndex);
|
|
||||||
|
|
||||||
// Deck section selection
|
|
||||||
selectSchemeDeck(playerIndex);
|
|
||||||
selectPlanarDeck(playerIndex);
|
|
||||||
selectVanguardAvatar(playerIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectMainDeck(final int playerIndex) {
|
private void selectMainDeck(final FDeckChooser mainChooser, final int playerIndex, final boolean isCommanderDeck) {
|
||||||
if (hasVariant(GameType.Commander) || hasVariant(GameType.Oathbreaker) || hasVariant(GameType.TinyLeaders) || hasVariant(GameType.Brawl)) {
|
|
||||||
// These game types use specific deck panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final FDeckChooser mainChooser = getDeckChooser(playerIndex);
|
|
||||||
final DeckType type = mainChooser.getSelectedDeckType();
|
final DeckType type = mainChooser.getSelectedDeckType();
|
||||||
final Deck deck = mainChooser.getDeck();
|
final Deck deck = mainChooser.getDeck();
|
||||||
// something went wrong, clear selection to prevent error loop
|
// something went wrong, clear selection to prevent error loop
|
||||||
@@ -561,75 +454,11 @@ public class VLobby implements ILobbyView {
|
|||||||
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
||||||
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
||||||
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
||||||
playerPanels.get(playerIndex).setDeckSelectorButtonText(text);
|
if (isCommanderDeck) {
|
||||||
fireDeckChangeListener(playerIndex, deck);
|
getPlayerPanel(playerIndex).setCommanderDeckSelectorButtonText(text);
|
||||||
}
|
} else {
|
||||||
mainChooser.saveState();
|
getPlayerPanel(playerIndex).setDeckSelectorButtonText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectCommanderDeck(final int playerIndex) {
|
|
||||||
if (!hasVariant(GameType.Commander) && !hasVariant(GameType.Oathbreaker) && !hasVariant(GameType.TinyLeaders) && !hasVariant(GameType.Brawl)) {
|
|
||||||
// Only these game types use this specific deck panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final FDeckChooser mainChooser = getCommanderDeckChooser(playerIndex);
|
|
||||||
final DeckType type = mainChooser.getSelectedDeckType();
|
|
||||||
final Deck deck = mainChooser.getDeck();
|
|
||||||
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
|
||||||
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
|
||||||
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
|
||||||
playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
|
|
||||||
fireDeckChangeListener(playerIndex, deck);
|
|
||||||
}
|
|
||||||
mainChooser.saveState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectOathbreakerDeck(final int playerIndex) {
|
|
||||||
if (!hasVariant(GameType.Oathbreaker)) {
|
|
||||||
// Only these game types use this specific deck panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final FDeckChooser mainChooser = getOathbreakerDeckChooser(playerIndex);
|
|
||||||
final DeckType type = mainChooser.getSelectedDeckType();
|
|
||||||
final Deck deck = mainChooser.getDeck();
|
|
||||||
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
|
||||||
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
|
||||||
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
|
||||||
playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
|
|
||||||
fireDeckChangeListener(playerIndex, deck);
|
|
||||||
}
|
|
||||||
mainChooser.saveState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectTinyLeadersDeck(final int playerIndex) {
|
|
||||||
if (!hasVariant(GameType.TinyLeaders)) {
|
|
||||||
// Only these game types use this specific deck panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final FDeckChooser mainChooser = getTinyLeaderDeckChooser(playerIndex);
|
|
||||||
final DeckType type = mainChooser.getSelectedDeckType();
|
|
||||||
final Deck deck = mainChooser.getDeck();
|
|
||||||
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
|
||||||
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
|
||||||
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
|
||||||
playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
|
|
||||||
fireDeckChangeListener(playerIndex, deck);
|
|
||||||
}
|
|
||||||
mainChooser.saveState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectBrawlDeck(final int playerIndex) {
|
|
||||||
if (!hasVariant(GameType.Brawl)) {
|
|
||||||
// Only these game types use this specific deck panel
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final FDeckChooser mainChooser = getBrawlDeckChooser(playerIndex);
|
|
||||||
final DeckType type = mainChooser.getSelectedDeckType();
|
|
||||||
final Deck deck = mainChooser.getDeck();
|
|
||||||
final Collection<DeckProxy> selectedDecks = mainChooser.getLstDecks().getSelectedItems();
|
|
||||||
if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) {
|
|
||||||
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
|
|
||||||
playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text);
|
|
||||||
fireDeckChangeListener(playerIndex, deck);
|
fireDeckChangeListener(playerIndex, deck);
|
||||||
}
|
}
|
||||||
mainChooser.saveState();
|
mainChooser.saveState();
|
||||||
@@ -653,7 +482,7 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sel.equals("Random")) {
|
if (sel.equals("Random")) {
|
||||||
final Deck randomDeck = RandomDeckGenerator.getRandomUserDeck(lobby, playerPanels.get(playerIndex).isAi());
|
final Deck randomDeck = RandomDeckGenerator.getRandomUserDeck(lobby, isPlayerAI(playerIndex));
|
||||||
schemePool = randomDeck.get(DeckSection.Schemes);
|
schemePool = randomDeck.get(DeckSection.Schemes);
|
||||||
}
|
}
|
||||||
} else if (selected instanceof Deck) {
|
} else if (selected instanceof Deck) {
|
||||||
@@ -684,7 +513,7 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sel.equals("Random")) {
|
if (sel.equals("Random")) {
|
||||||
final Deck randomDeck = RandomDeckGenerator.getRandomUserDeck(lobby, playerPanels.get(playerIndex).isAi());
|
final Deck randomDeck = RandomDeckGenerator.getRandomUserDeck(lobby, isPlayerAI(playerIndex));
|
||||||
planePool = randomDeck.get(DeckSection.Planes);
|
planePool = randomDeck.get(DeckSection.Planes);
|
||||||
}
|
}
|
||||||
} else if (selected instanceof Deck) {
|
} else if (selected instanceof Deck) {
|
||||||
@@ -703,7 +532,7 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Object selected = vgdAvatarLists.get(playerIndex).getSelectedValue();
|
final Object selected = vgdAvatarLists.get(playerIndex).getSelectedValue();
|
||||||
final PlayerPanel pp = playerPanels.get(playerIndex);
|
final PlayerPanel pp = getPlayerPanel(playerIndex);
|
||||||
final CardDetailPanel cdp = vgdAvatarDetails.get(playerIndex);
|
final CardDetailPanel cdp = vgdAvatarDetails.get(playerIndex);
|
||||||
|
|
||||||
PaperCard vanguardAvatar = null;
|
PaperCard vanguardAvatar = null;
|
||||||
@@ -726,7 +555,7 @@ public class VLobby implements ILobbyView {
|
|||||||
if (sel.contains("Use deck's default avatar") && deck != null && deck.has(DeckSection.Avatar)) {
|
if (sel.contains("Use deck's default avatar") && deck != null && deck.has(DeckSection.Avatar)) {
|
||||||
vanguardAvatar = deck.get(DeckSection.Avatar).get(0);
|
vanguardAvatar = deck.get(DeckSection.Avatar).get(0);
|
||||||
} else { //Only other string is "Random"
|
} else { //Only other string is "Random"
|
||||||
if (playerPanels.get(playerIndex).isAi()) { //AI
|
if (isPlayerAI(playerIndex)) { //AI
|
||||||
vanguardAvatar = Aggregates.random(getNonRandomAiAvatars());
|
vanguardAvatar = Aggregates.random(getNonRandomAiAvatars());
|
||||||
} else { //Human
|
} else { //Human
|
||||||
vanguardAvatar = Aggregates.random(getNonRandomHumanAvatars());
|
vanguardAvatar = Aggregates.random(getNonRandomHumanAvatars());
|
||||||
@@ -750,8 +579,8 @@ public class VLobby implements ILobbyView {
|
|||||||
|
|
||||||
switch (forGameType) {
|
switch (forGameType) {
|
||||||
case Constructed:
|
case Constructed:
|
||||||
decksFrame.add(deckChoosers.get(playerWithFocus), "grow, push");
|
decksFrame.add(getDeckChooser(playerWithFocus), "grow, push");
|
||||||
if (deckChoosers.get(playerWithFocus).getSelectedDeckType().toString().contains(localizer.getMessage("lblRandom"))) {
|
if (getDeckChooser(playerWithFocus).getSelectedDeckType().toString().contains(localizer.getMessage("lblRandom"))) {
|
||||||
final String strCheckboxConstraints = "h 30px!, gap 0 20px 0 0";
|
final String strCheckboxConstraints = "h 30px!, gap 0 20px 0 0";
|
||||||
decksFrame.add(cbSingletons, strCheckboxConstraints);
|
decksFrame.add(cbSingletons, strCheckboxConstraints);
|
||||||
decksFrame.add(cbArtifacts, strCheckboxConstraints);
|
decksFrame.add(cbArtifacts, strCheckboxConstraints);
|
||||||
@@ -766,16 +595,10 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Commander:
|
case Commander:
|
||||||
decksFrame.add(commanderDeckChoosers.get(playerWithFocus), "grow, push");
|
|
||||||
break;
|
|
||||||
case Oathbreaker:
|
case Oathbreaker:
|
||||||
decksFrame.add(oathbreakerDeckChoosers.get(playerWithFocus), "grow, push");
|
|
||||||
break;
|
|
||||||
case TinyLeaders:
|
case TinyLeaders:
|
||||||
decksFrame.add(tinyLeadersDeckChoosers.get(playerWithFocus), "grow, push");
|
|
||||||
break;
|
|
||||||
case Brawl:
|
case Brawl:
|
||||||
decksFrame.add(brawlDeckChoosers.get(playerWithFocus), "grow, push");
|
decksFrame.add(getDeckChooser(playerWithFocus), "grow, push");
|
||||||
break;
|
break;
|
||||||
case Planechase:
|
case Planechase:
|
||||||
decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push");
|
decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push");
|
||||||
@@ -798,7 +621,13 @@ public class VLobby implements ILobbyView {
|
|||||||
public LblHeader getLblTitle() { return lblTitle; }
|
public LblHeader getLblTitle() { return lblTitle; }
|
||||||
public JPanel getConstructedFrame() { return constructedFrame; }
|
public JPanel getConstructedFrame() { return constructedFrame; }
|
||||||
public JPanel getPanelStart() { return pnlStart; }
|
public JPanel getPanelStart() { return pnlStart; }
|
||||||
public List<FDeckChooser> getDeckChoosers() { return Collections.unmodifiableList(deckChoosers); }
|
public List<FDeckChooser> getDeckChoosers() {
|
||||||
|
List<FDeckChooser> choosers = new ArrayList<>(playerPanels.size());
|
||||||
|
for (final PlayerPanel playerPanel : playerPanels) {
|
||||||
|
choosers.add(playerPanel.getDeckChooser());
|
||||||
|
}
|
||||||
|
return choosers;
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets the random deck checkbox for Singletons. */
|
/** Gets the random deck checkbox for Singletons. */
|
||||||
FCheckBox getCbSingletons() { return cbSingletons; }
|
FCheckBox getCbSingletons() { return cbSingletons; }
|
||||||
@@ -816,29 +645,6 @@ public class VLobby implements ILobbyView {
|
|||||||
return iPlayer == playerWithFocus;
|
return iPlayer == playerWithFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final FDeckChooser getDeckChooser(final int playernum) {
|
|
||||||
return deckChoosers.get(playernum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final FDeckChooser getCommanderDeckChooser(final int playernum) {
|
|
||||||
return commanderDeckChoosers.get(playernum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final FDeckChooser getOathbreakerDeckChooser(final int playernum) {
|
|
||||||
return oathbreakerDeckChoosers.get(playernum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final FDeckChooser getTinyLeaderDeckChooser(final int playernum) {
|
|
||||||
return tinyLeadersDeckChoosers.get(playernum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final FDeckChooser getBrawlDeckChooser(final int playernum) {
|
|
||||||
return brawlDeckChoosers.get(playernum);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameType getCurrentGameMode() {
|
|
||||||
return lobby.getGameType();
|
|
||||||
}
|
|
||||||
void setCurrentGameMode(final GameType mode) {
|
void setCurrentGameMode(final GameType mode) {
|
||||||
lobby.setGameType(mode);
|
lobby.setGameType(mode);
|
||||||
update(true);
|
update(true);
|
||||||
@@ -851,10 +657,6 @@ public class VLobby implements ILobbyView {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumPlayers() {
|
|
||||||
return activePlayersNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Revalidates the player and deck sections. Necessary after adding or hiding any panels. */
|
/** Revalidates the player and deck sections. Necessary after adding or hiding any panels. */
|
||||||
private void refreshPanels(final boolean refreshPlayerFrame, final boolean refreshDeckFrame) {
|
private void refreshPanels(final boolean refreshPlayerFrame, final boolean refreshDeckFrame) {
|
||||||
if (refreshPlayerFrame) {
|
if (refreshPlayerFrame) {
|
||||||
@@ -888,8 +690,8 @@ public class VLobby implements ILobbyView {
|
|||||||
|
|
||||||
/** Saves avatar prefs for players one and two. */
|
/** Saves avatar prefs for players one and two. */
|
||||||
void updateAvatarPrefs() {
|
void updateAvatarPrefs() {
|
||||||
final int pOneIndex = playerPanels.get(0).getAvatarIndex();
|
final int pOneIndex = getPlayerPanel(0).getAvatarIndex();
|
||||||
final int pTwoIndex = playerPanels.get(1).getAvatarIndex();
|
final int pTwoIndex = getPlayerPanel(1).getAvatarIndex();
|
||||||
|
|
||||||
prefs.setPref(FPref.UI_AVATARS, pOneIndex + "," + pTwoIndex);
|
prefs.setPref(FPref.UI_AVATARS, pOneIndex + "," + pTwoIndex);
|
||||||
prefs.save();
|
prefs.save();
|
||||||
@@ -897,8 +699,8 @@ public class VLobby implements ILobbyView {
|
|||||||
|
|
||||||
/** Saves sleeve prefs for players one and two. */
|
/** Saves sleeve prefs for players one and two. */
|
||||||
void updateSleevePrefs() {
|
void updateSleevePrefs() {
|
||||||
final int pOneIndex = playerPanels.get(0).getSleeveIndex();
|
final int pOneIndex = getPlayerPanel(0).getSleeveIndex();
|
||||||
final int pTwoIndex = playerPanels.get(1).getSleeveIndex();
|
final int pTwoIndex = getPlayerPanel(1).getSleeveIndex();
|
||||||
|
|
||||||
prefs.setPref(FPref.UI_SLEEVES, pOneIndex + "," + pTwoIndex);
|
prefs.setPref(FPref.UI_SLEEVES, pOneIndex + "," + pTwoIndex);
|
||||||
prefs.save();
|
prefs.save();
|
||||||
@@ -925,7 +727,6 @@ public class VLobby implements ILobbyView {
|
|||||||
return usedSleeves;
|
return usedSleeves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final ImmutableList<String> genderOptions = ImmutableList.of("Male", "Female", "Any"),
|
private static final ImmutableList<String> genderOptions = ImmutableList.of("Male", "Female", "Any"),
|
||||||
typeOptions = ImmutableList.of("Fantasy", "Generic", "Any");
|
typeOptions = ImmutableList.of("Fantasy", "Generic", "Any");
|
||||||
final String getNewName() {
|
final String getNewName() {
|
||||||
@@ -973,18 +774,57 @@ public class VLobby implements ILobbyView {
|
|||||||
this.variant = variantType;
|
this.variant = variantType;
|
||||||
|
|
||||||
setToolTipText(variantType.getDescription());
|
setToolTipText(variantType.getDescription());
|
||||||
addItemListener(new ItemListener() {
|
addItemListener(e -> {
|
||||||
@Override public final void itemStateChanged(final ItemEvent e) {
|
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
lobby.applyVariant(variantType);
|
||||||
lobby.applyVariant(variantType);
|
} else {
|
||||||
} else {
|
lobby.removeVariant(variantType);
|
||||||
lobby.removeVariant(variantType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
VLobby.this.update(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FDeckChooser createDeckChooser(final GameType type, final int iSlot, final boolean ai) {
|
||||||
|
boolean forCommander;
|
||||||
|
DeckType deckType;
|
||||||
|
FPref prefKey;
|
||||||
|
switch (type) {
|
||||||
|
case Commander:
|
||||||
|
forCommander = true;
|
||||||
|
deckType = iSlot == 0 ? DeckType.COMMANDER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
||||||
|
prefKey = FPref.COMMANDER_DECK_STATES[iSlot];
|
||||||
|
break;
|
||||||
|
case TinyLeaders:
|
||||||
|
forCommander = true;
|
||||||
|
deckType = iSlot == 0 ? DeckType.TINY_LEADERS_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
||||||
|
prefKey = FPref.TINY_LEADER_DECK_STATES[iSlot];
|
||||||
|
break;
|
||||||
|
case Oathbreaker:
|
||||||
|
forCommander = true;
|
||||||
|
deckType = iSlot == 0 ? DeckType.OATHBREAKER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
|
||||||
|
prefKey = FPref.OATHBREAKER_DECK_STATES[iSlot];
|
||||||
|
break;
|
||||||
|
case Brawl:
|
||||||
|
forCommander = true;
|
||||||
|
deckType = iSlot == 0 ? DeckType.BRAWL_DECK : DeckType.CUSTOM_DECK;
|
||||||
|
prefKey = FPref.BRAWL_DECK_STATES[iSlot];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
forCommander = false;
|
||||||
|
deckType = iSlot == 0 ? DeckType.PRECONSTRUCTED_DECK : DeckType.COLOR_DECK;
|
||||||
|
prefKey = FPref.CONSTRUCTED_DECK_STATES[iSlot];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return cachedDeckChoosers.computeIfAbsent(prefKey, (key) -> {
|
||||||
|
final GameType gameType = forCommander ? type : GameType.Constructed;
|
||||||
|
final FDeckChooser fdc = new FDeckChooser(null, ai, gameType, forCommander);
|
||||||
|
fdc.initialize(prefKey, deckType);
|
||||||
|
fdc.getLstDecks().setSelectCommand(() -> selectMainDeck(fdc, iSlot, forCommander));
|
||||||
|
return fdc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final ActionListener nameListener = new ActionListener() {
|
final ActionListener nameListener = new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
@@ -1007,7 +847,7 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPlayerArchenemy(final int playernum) {
|
public boolean isPlayerArchenemy(final int playernum) {
|
||||||
return playerPanels.get(playernum).isArchenemy();
|
return getPlayerPanel(playernum).isArchenemy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the list of Vanguard avatar lists. */
|
/** Gets the list of Vanguard avatar lists. */
|
||||||
@@ -1027,11 +867,6 @@ public class VLobby implements ILobbyView {
|
|||||||
return vgdAllAvatars;
|
return vgdAllAvatars;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the Vanguard avatars not flagged RemoveDeck:All. */
|
|
||||||
public List<PaperCard> getAllAiAvatars() {
|
|
||||||
return vgdAllAiAvatars;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the Vanguard avatars not flagged RemoveDeck:Random. */
|
/** Return the Vanguard avatars not flagged RemoveDeck:Random. */
|
||||||
public List<PaperCard> getNonRandomHumanAvatars() {
|
public List<PaperCard> getNonRandomHumanAvatars() {
|
||||||
return nonRandomHumanAvatars;
|
return nonRandomHumanAvatars;
|
||||||
@@ -1055,7 +890,6 @@ public class VLobby implements ILobbyView {
|
|||||||
}
|
}
|
||||||
if (!cp.getRules().getAiHints().getRemAIDecks()) {
|
if (!cp.getRules().getAiHints().getRemAIDecks()) {
|
||||||
aiListData.add(cp);
|
aiListData.add(cp);
|
||||||
vgdAllAiAvatars.add(cp);
|
|
||||||
if (!cp.getRules().getAiHints().getRemRandomDecks()) {
|
if (!cp.getRules().getAiHints().getRemRandomDecks()) {
|
||||||
nonRandomAiAvatars.add(cp);
|
nonRandomAiAvatars.add(cp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,31 +28,32 @@ public final class CDev implements ICDoc {
|
|||||||
this.view = new VDev(this);
|
this.view = new VDev(this);
|
||||||
addListener(view);
|
addListener(view);
|
||||||
|
|
||||||
view.getLblUnlimitedLands().addMouseListener(madUnlimited);
|
view.getLblUnlimitedLands().addMouseListener(onClick(this::togglePlayManyLandsPerTurn));
|
||||||
view.getLblViewAll().addMouseListener(madViewAll);
|
view.getLblViewAll().addMouseListener(onClick(this::toggleViewAllCards));
|
||||||
view.getLblGenerateMana().addMouseListener(madMana);
|
view.getLblGenerateMana().addMouseListener(onClick(this::generateMana));
|
||||||
view.getLblSetupGame().addMouseListener(madSetup);
|
view.getLblSetupGame().addMouseListener(onClick(this::setupGameState));
|
||||||
view.getLblDumpGame().addMouseListener(madDump);
|
view.getLblDumpGame().addMouseListener(onClick(this::dumpGameState));
|
||||||
view.getLblTutor().addMouseListener(madTutor);
|
view.getLblTutor().addMouseListener(onClick(this::tutorForCard));
|
||||||
view.getLblCardToHand().addMouseListener(madCardToHand);
|
view.getLblCardToHand().addMouseListener(onClick(this::addCardToHand));
|
||||||
view.getLblExileFromHand().addMouseListener(madExileFromHand);
|
view.getLblExileFromHand().addMouseListener(onClick(this::exileCardsFromHand));
|
||||||
view.getLblCardToBattlefield().addMouseListener(madCardToBattlefield);
|
view.getLblCardToBattlefield().addMouseListener(onClick(this::addCardToBattlefield));
|
||||||
view.getLblCardToLibrary().addMouseListener(madCardToLibrary);
|
view.getLblCardToLibrary().addMouseListener(onClick(this::addCardToLibrary));
|
||||||
view.getLblCardToGraveyard().addMouseListener(madCardToGraveyard);
|
view.getLblCardToGraveyard().addMouseListener(onClick(this::addCardToGraveyard));
|
||||||
view.getLblCardToExile().addMouseListener(madCardToExile);
|
view.getLblCardToExile().addMouseListener(onClick(this::addCardToExile));
|
||||||
view.getLblCastSpell().addMouseListener(madCastASpell);
|
view.getLblCastSpell().addMouseListener(onClick(this::castASpell));
|
||||||
view.getLblRepeatAddCard().addMouseListener(madRepeatAddCard);
|
view.getLblRepeatAddCard().addMouseListener(onClick(this::repeatAddCard));
|
||||||
view.getLblAddCounterPermanent().addMouseListener(madAddCounter);
|
view.getLblAddCounterPermanent().addMouseListener(onClick(this::addCounterToPermanent));
|
||||||
view.getLblSubCounterPermanent().addMouseListener(madSubCounter);
|
view.getLblSubCounterPermanent().addMouseListener(onClick(this::removeCountersFromPermanent));
|
||||||
view.getLblTapPermanent().addMouseListener(madTap);
|
view.getLblTapPermanent().addMouseListener(onClick(this::tapPermanent));
|
||||||
view.getLblUntapPermanent().addMouseListener(madUntap);
|
view.getLblUntapPermanent().addMouseListener(onClick(this::untapPermanent));
|
||||||
view.getLblSetLife().addMouseListener(madLife);
|
view.getLblSetLife().addMouseListener(onClick(this::setPlayerLife));
|
||||||
view.getLblWinGame().addMouseListener(madWinGame);
|
view.getLblWinGame().addMouseListener(onClick(this::winGame));
|
||||||
view.getLblExileFromPlay().addMouseListener(madExileFromPlay);
|
view.getLblExileFromPlay().addMouseListener(onClick(this::exileCardsFromPlay));
|
||||||
view.getLblRemoveFromGame().addMouseListener(madRemoveFromGame);
|
view.getLblRemoveFromGame().addMouseListener(onClick(this::removeCardsFromGame));
|
||||||
view.getLblRiggedRoll().addMouseListener(madRiggedRoll);
|
view.getLblRiggedRoll().addMouseListener(onClick(this::riggedPlanerRoll));
|
||||||
view.getLblWalkTo().addMouseListener(madWalkToPlane);
|
view.getLblWalkTo().addMouseListener(onClick(this::planeswalkTo));
|
||||||
view.getLblAskAI().addMouseListener(madAskAI);
|
view.getLblAskAI().addMouseListener(onClick(this::askAI));
|
||||||
|
view.getLblAskSimulationAI().addMouseListener(onClick(this::askSimulationAI));
|
||||||
}
|
}
|
||||||
public IGameController getController() {
|
public IGameController getController() {
|
||||||
return matchUI.getGameController();
|
return matchUI.getGameController();
|
||||||
@@ -66,258 +67,99 @@ public final class CDev implements ICDoc {
|
|||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madUnlimited = new MouseAdapter() {
|
private MouseListener onClick(Runnable r) {
|
||||||
@Override
|
return new MouseAdapter() {
|
||||||
public void mousePressed(final MouseEvent e) {
|
@Override
|
||||||
togglePlayManyLandsPerTurn();
|
public void mousePressed(final MouseEvent e) {
|
||||||
}
|
r.run();
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void togglePlayManyLandsPerTurn() {
|
public void togglePlayManyLandsPerTurn() {
|
||||||
final boolean newValue = !view.getLblUnlimitedLands().getToggled();
|
final boolean newValue = !view.getLblUnlimitedLands().getToggled();
|
||||||
getController().cheat().setCanPlayUnlimitedLands(newValue);
|
getController().cheat().setCanPlayUnlimitedLands(newValue);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madViewAll = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
toggleViewAllCards();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void toggleViewAllCards() {
|
public void toggleViewAllCards() {
|
||||||
final boolean newValue = !view.getLblViewAll().getToggled();
|
final boolean newValue = !view.getLblViewAll().getToggled();
|
||||||
getController().cheat().setViewAllCards(newValue);
|
getController().cheat().setViewAllCards(newValue);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madMana = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
generateMana();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void generateMana() {
|
public void generateMana() {
|
||||||
getController().cheat().generateMana();
|
getController().cheat().generateMana();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madSetup = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
setupGameState();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void setupGameState() {
|
public void setupGameState() {
|
||||||
getController().cheat().setupGameState();
|
getController().cheat().setupGameState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madDump = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
dumpGameState();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void dumpGameState() {
|
public void dumpGameState() {
|
||||||
getController().cheat().dumpGameState();
|
getController().cheat().dumpGameState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madTutor = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
tutorForCard();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void tutorForCard() {
|
public void tutorForCard() {
|
||||||
getController().cheat().tutorForCard();
|
getController().cheat().tutorForCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCardToHand = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCardToHand();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCardToHand() {
|
public void addCardToHand() {
|
||||||
getController().cheat().addCardToHand();
|
getController().cheat().addCardToHand();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCardToLibrary = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCardToLibrary();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCardToLibrary() {
|
public void addCardToLibrary() {
|
||||||
getController().cheat().addCardToLibrary();
|
getController().cheat().addCardToLibrary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCardToGraveyard = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCardToGraveyard();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCardToGraveyard() {
|
public void addCardToGraveyard() {
|
||||||
getController().cheat().addCardToGraveyard();
|
getController().cheat().addCardToGraveyard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCardToExile = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCardToExile();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCardToExile() {
|
public void addCardToExile() {
|
||||||
getController().cheat().addCardToExile();
|
getController().cheat().addCardToExile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCastASpell = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
castASpell();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void castASpell() {
|
public void castASpell() {
|
||||||
getController().cheat().castASpell();
|
getController().cheat().castASpell();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madRepeatAddCard = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
repeatAddCard();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void repeatAddCard() {
|
public void repeatAddCard() {
|
||||||
getController().cheat().repeatLastAddition();
|
getController().cheat().repeatLastAddition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madExileFromHand = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
exileCardsFromHand();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void exileCardsFromHand() {
|
public void exileCardsFromHand() {
|
||||||
getController().cheat().exileCardsFromHand();
|
getController().cheat().exileCardsFromHand();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madAddCounter = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCounterToPermanent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCounterToPermanent() {
|
public void addCounterToPermanent() {
|
||||||
getController().cheat().addCountersToPermanent();
|
getController().cheat().addCountersToPermanent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madSubCounter = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
removeCountersFromPermanent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void removeCountersFromPermanent() {
|
public void removeCountersFromPermanent() {
|
||||||
getController().cheat().removeCountersFromPermanent();
|
getController().cheat().removeCountersFromPermanent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madTap = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
tapPermanent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void tapPermanent() {
|
public void tapPermanent() {
|
||||||
getController().cheat().tapPermanents();
|
getController().cheat().tapPermanents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madUntap = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
untapPermanent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void untapPermanent() {
|
public void untapPermanent() {
|
||||||
getController().cheat().untapPermanents();
|
getController().cheat().untapPermanents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madLife = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
setPlayerLife();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void setPlayerLife() {
|
public void setPlayerLife() {
|
||||||
getController().cheat().setPlayerLife();
|
getController().cheat().setPlayerLife();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madWinGame = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
winGame();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void winGame() {
|
public void winGame() {
|
||||||
getController().cheat().winGame();
|
getController().cheat().winGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madCardToBattlefield = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
addCardToBattlefield();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void addCardToBattlefield() {
|
public void addCardToBattlefield() {
|
||||||
getController().cheat().addCardToBattlefield();
|
getController().cheat().addCardToBattlefield();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madExileFromPlay = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
exileCardsFromPlay();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void exileCardsFromPlay() {
|
public void exileCardsFromPlay() {
|
||||||
getController().cheat().exileCardsFromBattlefield();
|
getController().cheat().exileCardsFromBattlefield();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madRemoveFromGame = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
removeCardsFromGame();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void removeCardsFromGame() {
|
public void removeCardsFromGame() {
|
||||||
getController().cheat().removeCardsFromGame();
|
getController().cheat().removeCardsFromGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madRiggedRoll = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
riggedPlanerRoll();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void riggedPlanerRoll() {
|
public void riggedPlanerRoll() {
|
||||||
getController().cheat().riggedPlanarRoll();
|
getController().cheat().riggedPlanarRoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madWalkToPlane = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
planeswalkTo();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void planeswalkTo() {
|
public void planeswalkTo() {
|
||||||
getController().cheat().planeswalkTo();
|
getController().cheat().planeswalkTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MouseListener madAskAI = new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(final MouseEvent e) {
|
|
||||||
askAI();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public void askAI() {
|
public void askAI() {
|
||||||
getController().cheat().askAI();
|
getController().cheat().askAI(false);
|
||||||
|
}
|
||||||
|
public void askSimulationAI() {
|
||||||
|
getController().cheat().askAI(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//========== End mouse listener inits
|
//========== End mouse listener inits
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ public class VDev implements IVDoc<CDev>, IDevListener {
|
|||||||
private final DevLabel lblWalkTo = new DevLabel(Localizer.getInstance().getMessage("lblWalkTo"));
|
private final DevLabel lblWalkTo = new DevLabel(Localizer.getInstance().getMessage("lblWalkTo"));
|
||||||
|
|
||||||
private final DevLabel lblAskAI = new DevLabel(Localizer.getInstance().getMessage("lblAskAI"));
|
private final DevLabel lblAskAI = new DevLabel(Localizer.getInstance().getMessage("lblAskAI"));
|
||||||
|
private final DevLabel lblAskSimulationAI = new DevLabel(Localizer.getInstance().getMessage("lblAskSimulationAI"));
|
||||||
|
|
||||||
|
|
||||||
private final CDev controller;
|
private final CDev controller;
|
||||||
@@ -91,7 +92,6 @@ public class VDev implements IVDoc<CDev>, IDevListener {
|
|||||||
public VDev(final CDev controller) {
|
public VDev(final CDev controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|
||||||
final String constraints = "w 95%!, gap 0 0 4px 0";
|
|
||||||
final String halfConstraints = "w 47%!, gap 0 0 4px 0";
|
final String halfConstraints = "w 47%!, gap 0 0 4px 0";
|
||||||
final String halfConstraintsLeft = halfConstraints + ", split 2";
|
final String halfConstraintsLeft = halfConstraints + ", split 2";
|
||||||
viewport.setOpaque(false);
|
viewport.setOpaque(false);
|
||||||
@@ -113,13 +113,14 @@ public class VDev implements IVDoc<CDev>, IDevListener {
|
|||||||
viewport.add(this.lblWinGame, halfConstraints);
|
viewport.add(this.lblWinGame, halfConstraints);
|
||||||
viewport.add(this.lblAddCounterPermanent, halfConstraintsLeft);
|
viewport.add(this.lblAddCounterPermanent, halfConstraintsLeft);
|
||||||
viewport.add(this.lblSubCounterPermanent, halfConstraints);
|
viewport.add(this.lblSubCounterPermanent, halfConstraints);
|
||||||
viewport.add(this.lblSetupGame, halfConstraintsLeft);
|
|
||||||
viewport.add(this.lblDumpGame, halfConstraints);
|
|
||||||
viewport.add(this.lblTapPermanent, halfConstraintsLeft);
|
viewport.add(this.lblTapPermanent, halfConstraintsLeft);
|
||||||
viewport.add(this.lblUntapPermanent, halfConstraints);
|
viewport.add(this.lblUntapPermanent, halfConstraints);
|
||||||
viewport.add(this.lblRiggedRoll, halfConstraintsLeft);
|
viewport.add(this.lblRiggedRoll, halfConstraintsLeft);
|
||||||
viewport.add(this.lblWalkTo, halfConstraints);
|
viewport.add(this.lblWalkTo, halfConstraints);
|
||||||
viewport.add(this.lblAskAI, halfConstraintsLeft);
|
viewport.add(this.lblAskAI, halfConstraintsLeft);
|
||||||
|
viewport.add(this.lblAskSimulationAI, halfConstraintsLeft);
|
||||||
|
viewport.add(this.lblSetupGame, halfConstraintsLeft);
|
||||||
|
viewport.add(this.lblDumpGame, halfConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
//========= Overridden methods
|
//========= Overridden methods
|
||||||
@@ -302,20 +303,23 @@ public class VDev implements IVDoc<CDev>, IDevListener {
|
|||||||
return this.lblAskAI;
|
return this.lblAskAI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DevLabel getLblAskSimulationAI() {
|
||||||
|
return this.lblAskSimulationAI;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Labels that act as buttons which control dev mode functions. Labels are
|
* Labels that act as buttons which control dev mode functions. Labels are
|
||||||
* used to support multiline text.
|
* used to support multiline text.
|
||||||
*/
|
*/
|
||||||
public class DevLabel extends SkinnedLabel {
|
public static class DevLabel extends SkinnedLabel {
|
||||||
private static final long serialVersionUID = 7917311680519060700L;
|
private static final long serialVersionUID = 7917311680519060700L;
|
||||||
|
|
||||||
private FSkin.SkinColor defaultBG;
|
private FSkin.SkinColor defaultBG;
|
||||||
private final FSkin.SkinColor hoverBG = FSkin.getColor(FSkin.Colors.CLR_HOVER);
|
private final FSkin.SkinColor hoverBG = FSkin.getColor(FSkin.Colors.CLR_HOVER);
|
||||||
private final FSkin.SkinColor pressedBG = FSkin.getColor(FSkin.Colors.CLR_INACTIVE);
|
private final FSkin.SkinColor pressedBG = FSkin.getColor(FSkin.Colors.CLR_INACTIVE);
|
||||||
private boolean toggled;
|
private boolean toggled;
|
||||||
private int w, h; // Width, height, radius, insets (for paintComponent)
|
|
||||||
|
|
||||||
private final int r, i;
|
private final int r, i; // Radius, insets (for paintComponent)
|
||||||
|
|
||||||
public DevLabel(final String text0) {
|
public DevLabel(final String text0) {
|
||||||
super();
|
super();
|
||||||
@@ -380,10 +384,10 @@ public class VDev implements IVDoc<CDev>, IDevListener {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent(final Graphics g) {
|
protected void paintComponent(final Graphics g) {
|
||||||
this.w = this.getWidth();
|
int w = this.getWidth();
|
||||||
this.h = this.getHeight();
|
int h = this.getHeight();
|
||||||
g.setColor(this.getBackground());
|
g.setColor(this.getBackground());
|
||||||
g.fillRoundRect(this.i, this.i, this.w - (2 * this.i), this.h - this.i, this.r, this.r);
|
g.fillRoundRect(this.i, this.i, w - (2 * this.i), h - this.i, this.r, this.r);
|
||||||
super.paintComponent(g);
|
super.paintComponent(g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -483,4 +483,126 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
|||||||
AssertJUnit.assertEquals("Chaos Warp", sa.getHostCard().getName());
|
AssertJUnit.assertEquals("Chaos Warp", sa.getHostCard().getName());
|
||||||
AssertJUnit.assertEquals(expectedTarget, sa.getTargetCard());
|
AssertJUnit.assertEquals(expectedTarget, sa.getTargetCard());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoSimulationsWhenNoTargets() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
|
||||||
|
addCard("Island", p);
|
||||||
|
addCard("Island", p);
|
||||||
|
addCardToZone("Counterspell", p, ZoneType.Hand);
|
||||||
|
addCardToZone("Unsummon", p, ZoneType.Hand);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||||
|
AssertJUnit.assertNull(sa);
|
||||||
|
AssertJUnit.assertEquals(0, picker.getNumSimulations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLandDropPruning() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
|
||||||
|
addCardToZone("Island", p, ZoneType.Hand);
|
||||||
|
addCardToZone("Island", p, ZoneType.Hand);
|
||||||
|
addCardToZone("Island", p, ZoneType.Hand);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||||
|
AssertJUnit.assertNotNull(sa);
|
||||||
|
// Only one land drop should be simulated, since the cards are identical.
|
||||||
|
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpellCantTargetSelf() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
Player opponent = game.getPlayers().get(0);
|
||||||
|
|
||||||
|
addCardToZone("Unsubstantiate", p, ZoneType.Hand);
|
||||||
|
addCard("Forest", p);
|
||||||
|
addCard("Island", p);
|
||||||
|
Card expectedTarget = addCard("Flying Men", opponent);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||||
|
AssertJUnit.assertNotNull(sa);
|
||||||
|
AssertJUnit.assertEquals(expectedTarget, sa.getTargetCard());
|
||||||
|
// Only a single simulation expected (no target self).
|
||||||
|
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModalSpellCantTargetSelf() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
Player opponent = game.getPlayers().get(0);
|
||||||
|
|
||||||
|
addCardToZone("Decisive Denial", p, ZoneType.Hand);
|
||||||
|
addCard("Forest", p);
|
||||||
|
addCard("Island", p);
|
||||||
|
addCard("Runeclaw Bear", p);
|
||||||
|
addCard("Flying Men", opponent);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||||
|
AssertJUnit.assertNotNull(sa);
|
||||||
|
// Expected: Runeclaw Bear fights Flying Men
|
||||||
|
// Only a single simulation expected (no target self).
|
||||||
|
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModalSpellNoTargetsForModeWithSubAbility() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
|
||||||
|
addCardToZone("Temur Charm", p, ZoneType.Hand);
|
||||||
|
addCard("Forest", p);
|
||||||
|
addCard("Island", p);
|
||||||
|
addCard("Mountain", p);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
picker.chooseSpellAbilityToPlay(null);
|
||||||
|
// Only mode "Creatures with power 3 or less can’t block this turn" should be simulated.
|
||||||
|
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModalSpellNoTargetsForAnyModes() {
|
||||||
|
Game game = initAndCreateGame();
|
||||||
|
Player p = game.getPlayers().get(1);
|
||||||
|
|
||||||
|
addCardToZone("Drown in the Loch", p, ZoneType.Hand);
|
||||||
|
addCard("Swamp", p);
|
||||||
|
addCard("Island", p);
|
||||||
|
|
||||||
|
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||||
|
game.getAction().checkStateEffects(true);
|
||||||
|
|
||||||
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
|
picker.chooseSpellAbilityToPlay(null);
|
||||||
|
// TODO: Ideally, this would be 0 simulations, but we currently only determine there are no
|
||||||
|
// valid modes in SpellAbilityChoicesIterator, which runs already when we're simulating.
|
||||||
|
// Still, this test case exercises the code path and ensures we don't crash in this case.
|
||||||
|
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class PointOfInterestChanges implements SaveFileContent {
|
|||||||
}
|
}
|
||||||
data.storeObject("keys",keys.toArray(new String[0]));
|
data.storeObject("keys",keys.toArray(new String[0]));
|
||||||
for(int i=0;i<items.size();i++)
|
for(int i=0;i<items.size();i++)
|
||||||
data.store("value_"+i,items.get(0).save());
|
data.store("value_"+i,items.get(i).save());
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ public class WorldSave {
|
|||||||
System.err.println("Generating New World");
|
System.err.println("Generating New World");
|
||||||
currentSave.world.generateNew(0);
|
currentSave.world.generateNew(0);
|
||||||
}
|
}
|
||||||
currentSave.pointOfInterestChanges.clear();
|
|
||||||
|
|
||||||
currentSave.onLoadList.emit();
|
currentSave.onLoadList.emit();
|
||||||
|
|
||||||
|
|||||||
@@ -1360,9 +1360,8 @@ public class FDeckChooser extends FScreen {
|
|||||||
if (StringUtils.isBlank(savedState)) {
|
if (StringUtils.isBlank(savedState)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
else {
|
final String[] parts = savedState.split(";", -1);
|
||||||
return Arrays.asList(savedState.split(";")[1].split(SELECTED_DECK_DELIMITER));
|
return Arrays.asList(parts[1].split(SELECTED_DECK_DELIMITER));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
System.err.println(ex + " [savedState=" + savedState + "]");
|
System.err.println(ex + " [savedState=" + savedState + "]");
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class FDeckViewer extends FScreen {
|
|||||||
deckList.append(s.toString()).append(": ");
|
deckList.append(s.toString()).append(": ");
|
||||||
deckList.append(nl);
|
deckList.append(nl);
|
||||||
for (final Entry<PaperCard, Integer> ev : cp) {
|
for (final Entry<PaperCard, Integer> ev : cp) {
|
||||||
deckList.append(ev.getValue()).append(" ").append(ev.getKey()).append(nl);
|
deckList.append(ev.getValue()).append(" ").append(ev.getKey().getCardName()).append(nl);
|
||||||
}
|
}
|
||||||
deckList.append(nl);
|
deckList.append(nl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:B R
|
|||||||
Types:Creature Sliver
|
Types:Creature Sliver
|
||||||
PT:2/2
|
PT:2/2
|
||||||
S:Mode$ Continuous | Affected$ Sliver | AddAbility$ Damage | Description$ All Slivers have "{2}, Sacrifice this permanent: This permanent deals 2 damage to any target."
|
S:Mode$ Continuous | Affected$ Sliver | AddAbility$ Damage | Description$ All Slivers have "{2}, Sacrifice this permanent: This permanent deals 2 damage to any target."
|
||||||
SVar:Damage:AB$DealDamage | Cost$ 2 Sac<1/CARDNAME> | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | SpellDescription$ CARDNAME deals 2 damage to any target.
|
SVar:Damage:AB$ DealDamage | Cost$ 2 Sac<1/CARDNAME> | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | SpellDescription$ CARDNAME deals 2 damage to any target.
|
||||||
SVar:BuffedBy:Sliver
|
SVar:BuffedBy:Sliver
|
||||||
Oracle:All Slivers have "{2}, Sacrifice this permanent: This permanent deals 2 damage to any target."
|
Oracle:All Slivers have "{2}, Sacrifice this permanent: This permanent deals 2 damage to any target."
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ SVar:VolverStrength:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | Counter
|
|||||||
SVar:VolverLaunch:DB$ Animate | Defined$ Self | Keywords$ Flying | Duration$ Permanent
|
SVar:VolverLaunch:DB$ Animate | Defined$ Self | Keywords$ Flying | Duration$ Permanent
|
||||||
SVar:VolverPumped:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ETB$ True | SubAbility$ VolverResilience | SpellDescription$ If CARDNAME was kicked with its {B} kicker, it enters the battlefield with a +1/+1 counter on it and with "Pay 3 life: Regenerate CARDNAME."
|
SVar:VolverPumped:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ETB$ True | SubAbility$ VolverResilience | SpellDescription$ If CARDNAME was kicked with its {B} kicker, it enters the battlefield with a +1/+1 counter on it and with "Pay 3 life: Regenerate CARDNAME."
|
||||||
SVar:VolverResilience:DB$ Animate | Defined$ Self | Abilities$ ABRegen | Duration$ Permanent
|
SVar:VolverResilience:DB$ Animate | Defined$ Self | Abilities$ ABRegen | Duration$ Permanent
|
||||||
SVar:ABRegen:AB$Regenerate | Cost$ PayLife<3> | SpellDescription$ Regenerate CARDNAME.
|
SVar:ABRegen:AB$ Regenerate | Cost$ PayLife<3> | SpellDescription$ Regenerate CARDNAME.
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
DeckNeeds:Color$Blue|Black
|
DeckNeeds:Color$Blue|Black
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Enchantment Aura
|
|||||||
K:Enchant land
|
K:Enchant land
|
||||||
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Land | AILogic$ Pump
|
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Land | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Land.AttachedBy | AddAbility$ GainLife | AddSVar$ AnimalBoneyardX | Description$ Enchanted land has "{T}, Sacrifice a creature: You gain life equal to the sacrificed creature's toughness."
|
S:Mode$ Continuous | Affected$ Land.AttachedBy | AddAbility$ GainLife | AddSVar$ AnimalBoneyardX | Description$ Enchanted land has "{T}, Sacrifice a creature: You gain life equal to the sacrificed creature's toughness."
|
||||||
SVar:GainLife:AB$GainLife | Cost$ T Sac<1/Creature> | LifeAmount$ AnimalBoneyardX | SpellDescription$ You gain life equal to the sacrificed creature's toughness.
|
SVar:GainLife:AB$ GainLife | Cost$ T Sac<1/Creature> | LifeAmount$ AnimalBoneyardX | SpellDescription$ You gain life equal to the sacrificed creature's toughness.
|
||||||
SVar:AnimalBoneyardX:Sacrificed$CardToughness
|
SVar:AnimalBoneyardX:Sacrificed$CardToughness
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:NonStackingAttachEffect:True
|
SVar:NonStackingAttachEffect:True
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ ManaCost:1 B
|
|||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
K:Enchant creature card in a graveyard
|
K:Enchant creature card in a graveyard
|
||||||
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
|
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Return enchanted creature card to the battlefield under your control and attach CARDNAME to it.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Return enchanted creature card to the battlefield under your control and attach CARDNAME to it. When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||||
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | GainControl$ True | SubAbility$ DBAnimate
|
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | GainControl$ True | SubAbility$ DBAnimate
|
||||||
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Duration$ Permanent | SubAbility$ DBAttach
|
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Duration$ Permanent | SubAbility$ DBAttach
|
||||||
SVar:DBAttach:DB$ Attach | Defined$ Remembered
|
SVar:DBAttach:DB$ Attach | Defined$ Remembered | SubAbility$ DBDelay
|
||||||
|
SVar:DBDelay:DB$ DelayedTrigger | Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Execute$ TrigSacrifice | RememberObjects$ RememberedLKI | TriggerDescription$ When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||||
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
|
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSacrifice | TriggerDescription$ When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
|
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DelayTriggerRememberedLKI
|
||||||
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DirectRemembered | SubAbility$ DBCleanup
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ -1 | Description$ Enchanted creature gets -1/-0.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ -1 | Description$ Enchanted creature gets -1/-0.
|
||||||
DeckHas:Ability$Graveyard
|
DeckHas:Ability$Graveyard
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ Types:Enchantment Aura
|
|||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
A:SP$ Attach | Cost$ 2 R | ValidTgts$ Creature | AILogic$ Pump
|
A:SP$ Attach | Cost$ 2 R | ValidTgts$ Creature | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddAbility$ Damage | Description$ Enchanted creature gets +2/+2 and has "{T}: This creature deals 1 damage to any target."
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddAbility$ Damage | Description$ Enchanted creature gets +2/+2 and has "{T}: This creature deals 1 damage to any target."
|
||||||
SVar:Damage:AB$DealDamage | Cost$ T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target.
|
SVar:Damage:AB$ DealDamage | Cost$ T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target.
|
||||||
Oracle:Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.)\nEnchanted creature gets +2/+2 and has "{T}: This creature deals 1 damage to any target."
|
Oracle:Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.)\nEnchanted creature gets +2/+2 and has "{T}: This creature deals 1 damage to any target."
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Enchantment Aura
|
|||||||
K:Enchant land
|
K:Enchant land
|
||||||
A:SP$ Attach | Cost$ 2 R R | ValidTgts$ Land | AILogic$ Pump
|
A:SP$ Attach | Cost$ 2 R R | ValidTgts$ Land | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Land.EnchantedBy | AddAbility$ Damage | Description$ Enchanted land has "{T}: This land deals 1 damage to any target."
|
S:Mode$ Continuous | Affected$ Land.EnchantedBy | AddAbility$ Damage | Description$ Enchanted land has "{T}: This land deals 1 damage to any target."
|
||||||
SVar:Damage:AB$DealDamage | Cost$ T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target.
|
SVar:Damage:AB$ DealDamage | Cost$ T | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to any target.
|
||||||
SVar:NonStackingAttachEffect:True
|
SVar:NonStackingAttachEffect:True
|
||||||
Oracle:Enchant land\nEnchanted land has "{T}: This land deals 1 damage to any target."
|
Oracle:Enchant land\nEnchanted land has "{T}: This land deals 1 damage to any target."
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:2 B
|
|||||||
Types:Creature Sliver
|
Types:Creature Sliver
|
||||||
PT:2/2
|
PT:2/2
|
||||||
S:Mode$ Continuous | Affected$ Sliver | AddAbility$ Mana | Description$ All Slivers have "Sacrifice this permanent: Add {B}{B}."
|
S:Mode$ Continuous | Affected$ Sliver | AddAbility$ Mana | Description$ All Slivers have "Sacrifice this permanent: Add {B}{B}."
|
||||||
SVar:Mana:AB$Mana | Cost$ Sac<1/CARDNAME> | Produced$ B | Amount$ 2 | SpellDescription$ Add {B}{B}.
|
SVar:Mana:AB$ Mana | Cost$ Sac<1/CARDNAME> | Produced$ B | Amount$ 2 | SpellDescription$ Add {B}{B}.
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
Oracle:All Slivers have "Sacrifice this permanent: Add {B}{B}."
|
Oracle:All Slivers have "Sacrifice this permanent: Add {B}{B}."
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ Types:Creature Vampire
|
|||||||
PT:2/2
|
PT:2/2
|
||||||
K:Haste
|
K:Haste
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<2 R\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<2 R\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | RememberSVarAmount$ NumTimes | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ NumTimes | SubAbility$ DBExile
|
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ X | SubAbility$ DBExile
|
||||||
SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | TargetMin$ 0 | TargetMax$ NumTimes | ValidTgts$ Instant.cmcLE3+YouOwn,Sorcery.cmcLE3+YouOwn | TgtPrompt$ Select up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard | RememberChanged$ True | SubAbility$ DBCopyCast
|
SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | TargetMin$ 0 | TargetMax$ X | ValidTgts$ Instant.cmcLE3+YouOwn,Sorcery.cmcLE3+YouOwn | TgtPrompt$ Select up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard | RememberChanged$ True | SubAbility$ DBCopyCast
|
||||||
SVar:DBCopyCast:DB$ Play | Valid$ Card.IsRemembered | ValidZone$ Exile | Controller$ You | CopyCard$ True | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | Amount$ All | SubAbility$ ExileMe
|
SVar:DBCopyCast:DB$ Play | Valid$ Card.IsRemembered | ValidZone$ Exile | Controller$ You | CopyCard$ True | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | Amount$ All | SubAbility$ DBCleanup
|
||||||
SVar:ExileMe:DB$ ChangeZoneAll | Origin$ Stack | Destination$ Exile | ChangeType$ Card.Self | SubAbility$ DBCleanup
|
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:NumTimes:Number$0
|
SVar:X:Count$TriggerRememberAmount
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
Oracle:Haste\nWhen Bloodthirsty Adversary enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Bloodthirsty Adversary, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
Oracle:Haste\nWhen Bloodthirsty Adversary enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Bloodthirsty Adversary, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ SVar:RepeatOpp:DB$ RepeatEach | RepeatSubAbility$ ChooseCardsToTap | RepeatPlaye
|
|||||||
SVar:ChooseCardsToTap:DB$ ChooseCard | Defined$ Opponent | MinAmount$ 0 | Amount$ NumCreatures | Choices$ Creature.untapped+RememberedPlayerCtrl | ChoiceTitle$ Choose any number of untapped creatures you control | ChoiceZone$ Battlefield | RememberChosen$ True | AILogic$ BowToMyCommand | SubAbility$ TapChosenCards
|
SVar:ChooseCardsToTap:DB$ ChooseCard | Defined$ Opponent | MinAmount$ 0 | Amount$ NumCreatures | Choices$ Creature.untapped+RememberedPlayerCtrl | ChoiceTitle$ Choose any number of untapped creatures you control | ChoiceZone$ Battlefield | RememberChosen$ True | AILogic$ BowToMyCommand | SubAbility$ TapChosenCards
|
||||||
SVar:TapChosenCards:DB$ Tap | Defined$ Remembered | SubAbility$ AbandonSelf | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8
|
SVar:TapChosenCards:DB$ Tap | Defined$ Remembered | SubAbility$ AbandonSelf | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8
|
||||||
SVar:AbandonSelf:DB$ Abandon | SubAbility$ DBCleanup | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8
|
SVar:AbandonSelf:DB$ Abandon | SubAbility$ DBCleanup | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8
|
||||||
T:Mode$ Abandoned | ValidCard$ Self | Execute$ DBCleanup
|
T:Mode$ Abandoned | ValidCard$ Card.Self | Execute$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
|
||||||
SVar:NumCreatures:Count$Valid Creature.RememberedPlayerCtrl
|
SVar:NumCreatures:Count$Valid Creature.RememberedPlayerCtrl
|
||||||
SVar:TappedCreaturePower:Count$SumPower_Card.IsRemembered
|
SVar:TappedCreaturePower:Count$SumPower_Card.IsRemembered
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ SVar:TrigAnimate:DB$ Animate | ValidTgts$ Permanent.nonLand+YouCtrl | TgtPrompt$
|
|||||||
SVar:CrashLand:Mode$ DamageDealtOnce | ValidSource$ Card.Self | ValidTarget$ Player,Permanent | Execute$ RollCounters | TriggerZones$ Battlefield | TriggerDescription$ Crash Land — Whenever this Vehicle deals damage, roll a six-sided die. If the result is equal to this Vehicle's mana value, sacrifice this Vehicle, then it deals that much damage to any target.
|
SVar:CrashLand:Mode$ DamageDealtOnce | ValidSource$ Card.Self | ValidTarget$ Player,Permanent | Execute$ RollCounters | TriggerZones$ Battlefield | TriggerDescription$ Crash Land — Whenever this Vehicle deals damage, roll a six-sided die. If the result is equal to this Vehicle's mana value, sacrifice this Vehicle, then it deals that much damage to any target.
|
||||||
SVar:RollCounters:DB$ RollDice | ResultSVar$ Result | SubAbility$ Crash
|
SVar:RollCounters:DB$ RollDice | ResultSVar$ Result | SubAbility$ Crash
|
||||||
SVar:Crash:DB$ Sacrifice | ConditionCheckSVar$ Result | ConditionSVarCompare$ EQY | SubAbility$ CrashDamage
|
SVar:Crash:DB$ Sacrifice | ConditionCheckSVar$ Result | ConditionSVarCompare$ EQY | SubAbility$ CrashDamage
|
||||||
SVar:CrashDamage:DB$ DealDamage | ValidTgt$ Planeswalker,Player,Permanent | TgtPromp$ Choose any target | NumDmg$ Y | ConditionCheckSVar$ Result | ConditionSVarCompare$ EQY
|
SVar:CrashDamage:DB$ DealDamage | ValidTgts$ Planeswalker,Player,Creature | TgtPrompt$ Choose any target | NumDmg$ Y | ConditionCheckSVar$ Result | ConditionSVarCompare$ EQY
|
||||||
SVar:X:Targeted$CardManaCost
|
SVar:X:Targeted$CardManaCost
|
||||||
SVar:Y:Count$CardManaCost
|
SVar:Y:Count$CardManaCost
|
||||||
DeckHints:Type$Vehicle
|
DeckHints:Type$Vehicle
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ K:Enchant creature
|
|||||||
A:SP$ Attach | Cost$ 3 W W W | ValidTgts$ Creature | AILogic$ Pump
|
A:SP$ Attach | Cost$ 3 W W W | ValidTgts$ Creature | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 3 | AddToughness$ 3 | Description$ Enchanted creature gets +3/+3.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 3 | AddToughness$ 3 | Description$ Enchanted creature gets +3/+3.
|
||||||
T:Mode$ DamageDone | ValidSource$ Card.AttachedBy | ValidTarget$ Player | Execute$ TrigSetLife | CombatDamage$ True | TriggerDescription$ Whenever enchanted creature deals combat damage to a player, double its controller's life total.
|
T:Mode$ DamageDone | ValidSource$ Card.AttachedBy | ValidTarget$ Player | Execute$ TrigSetLife | CombatDamage$ True | TriggerDescription$ Whenever enchanted creature deals combat damage to a player, double its controller's life total.
|
||||||
SVar:TrigSetLife:DB$ Pump | RememberObjects$ TriggeredSourceController | SubAbility$ DBSet
|
SVar:TrigSetLife:DB$ SetLife | Defined$ TriggeredSourceController | LifeAmount$ X
|
||||||
SVar:DBSet:DB$ SetLife | Defined$ Remembered | LifeAmount$ X | SubAbility$ DBCleanup
|
SVar:X:PlayerCountDefinedTriggeredSourceController$LifeTotal/Twice
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
|
||||||
SVar:X:PlayerCountRemembered$LifeTotal/Twice
|
|
||||||
Oracle:Enchant creature\nEnchanted creature gets +3/+3.\nWhenever enchanted creature deals combat damage to a player, double its controller's life total.
|
Oracle:Enchant creature\nEnchanted creature gets +3/+3.\nWhenever enchanted creature deals combat damage to a player, double its controller's life total.
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
Name:Cephalid Shrine
|
Name:Cephalid Shrine
|
||||||
ManaCost:1 U U
|
ManaCost:1 U U
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ Player | TriggerZones$ Battlefield | Execute$ TrigCounterRem | TriggerDescription$ Whenever a player casts a spell, counter that spell unless that player pays {X}, where X is the number of cards in all graveyards with the same name as the spell.
|
T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ Player | TriggerZones$ Battlefield | Execute$ TrigCounter | TriggerDescription$ Whenever a player casts a spell, counter that spell unless that player pays {X}, where X is the number of cards in all graveyards with the same name as the spell.
|
||||||
SVar:TrigCounterRem:DB$ Pump | RememberObjects$ TriggeredCard | SubAbility$ DBCounter
|
SVar:TrigCounter:DB$ Counter | Defined$ TriggeredSpellAbility | UnlessCost$ X | UnlessPayer$ TriggeredActivator
|
||||||
SVar:DBCounter:DB$ Counter | Defined$ TriggeredSpellAbility | UnlessCost$ X | UnlessPayer$ TriggeredActivator | SubAbility$ DBCleanup
|
SVar:X:Count$ValidGraveyard Card.sharesNameWith TriggeredCard
|
||||||
SVar:X:Count$ValidGraveyard Card.sharesNameWith Remembered
|
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Whenever a player casts a spell, counter that spell unless that player pays {X}, where X is the number of cards in all graveyards with the same name as the spell.
|
Oracle:Whenever a player casts a spell, counter that spell unless that player pays {X}, where X is the number of cards in all graveyards with the same name as the spell.
|
||||||
|
|||||||
@@ -5,5 +5,8 @@ PT:7/7
|
|||||||
K:Flash
|
K:Flash
|
||||||
K:Flying
|
K:Flying
|
||||||
K:This spell can't be countered.
|
K:This spell can't be countered.
|
||||||
A:AB$ Animate | Cost$ Discard<1/Card> | Types$ Human | Power$ 1 | Toughness$ 1 | Keywords$ Hexproof | HiddenKeywords$ Unblockable | RemoveAllAbilities$ True | RemoveCreatureTypes$ True | SpellDescription$ Until end of turn, CARDNAME becomes a Human with base power and toughness 1/1, loses all abilities, and gains hexproof. It can't be blocked this turn.
|
A:AB$ Animate | Cost$ Discard<1/Card> | Types$ Human | Power$ 1 | Toughness$ 1 | Keywords$ Hexproof | RemoveAllAbilities$ True | RemoveCreatureTypes$ True | SubAbility$ DBUnblockable | SpellDescription$ Until end of turn, CARDNAME becomes a Human with base power and toughness 1/1, loses all abilities, and gains hexproof. It can't be blocked this turn.
|
||||||
|
SVar:DBUnblockable:DB$ Effect | ExileOnMoved$ Battlefield | RememberObjects$ Self | StaticAbilities$ Unblockable
|
||||||
|
SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn.
|
||||||
|
DeckHas:Ability$Discard
|
||||||
Oracle:Flash\nThis spell can't be countered.\nFlying\nDiscard a card: Until end of turn, Chromium, the Mutable becomes a Human with base power and toughness 1/1, loses all abilities, and gains hexproof. It can't be blocked this turn.
|
Oracle:Flash\nThis spell can't be countered.\nFlying\nDiscard a card: Until end of turn, Chromium, the Mutable becomes a Human with base power and toughness 1/1, loses all abilities, and gains hexproof. It can't be blocked this turn.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$
|
|||||||
SVar:TrigDraw:AB$ Draw | Cost$ 1 Discard<1/Card>
|
SVar:TrigDraw:AB$ Draw | Cost$ 1 Discard<1/Card>
|
||||||
T:Mode$ DiscardedAll | ValidPlayer$ You | ValidCard$ Card.nonLand | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.
|
T:Mode$ DiscardedAll | ValidPlayer$ You | ValidCard$ Card.nonLand | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.
|
||||||
SVar:TrigEffect:AB$ Effect | Cost$ ExileFromGrave<1/Card.TriggeredCards> | RememberObjects$ ExiledCards | StaticAbilities$ MayCast | ExileOnMoved$ Stack
|
SVar:TrigEffect:AB$ Effect | Cost$ ExileFromGrave<1/Card.TriggeredCards> | RememberObjects$ ExiledCards | StaticAbilities$ MayCast | ExileOnMoved$ Stack
|
||||||
SVar:MayCast:Mode$ Continuous | Affected$ Card.IsRemembered | MayPlay$ True | EffectZone$ Command | AffectedZone$ Exile | Description$ You may cast this spell this turn.
|
SVar:MayCast:Mode$ Continuous | Affected$ Card.IsRemembered+nonLand | MayPlay$ True | EffectZone$ Command | AffectedZone$ Exile | Description$ You may cast this spell this turn.
|
||||||
SVar:HasAttackEffect:TRUE
|
SVar:HasAttackEffect:TRUE
|
||||||
DeckHas:Ability$Discard
|
DeckHas:Ability$Discard
|
||||||
Oracle:Whenever Conspiracy Theorist attacks, you may pay {1} and discard a card. If you do, draw a card.\nWhenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.
|
Oracle:Whenever Conspiracy Theorist attacks, you may pay {1} and discard a card. If you do, draw a card.\nWhenever you discard one or more nonland cards, you may exile one of them from your graveyard. If you do, you may cast it this turn.
|
||||||
|
|||||||
@@ -3,5 +3,8 @@ ManaCost:no cost
|
|||||||
Types:Land
|
Types:Land
|
||||||
K:CARDNAME enters the battlefield tapped.
|
K:CARDNAME enters the battlefield tapped.
|
||||||
A:AB$ Mana | Cost$ T | Produced$ Combo U B | SpellDescription$ Add {U} or {B}.
|
A:AB$ Mana | Cost$ T | Produced$ Combo U B | SpellDescription$ Add {U} or {B}.
|
||||||
A:AB$ Animate | Cost$ 1 U B | Defined$ Self | Power$ 3 | Toughness$ 2 | Types$ Creature,Elemental | Colors$ Blue,Black | HiddenKeywords$ Unblockable | SpellDescription$ CARDNAME becomes a 3/2 blue and black Elemental creature until end of turn and can't be blocked this turn. It's still a land.
|
A:AB$ Animate | Cost$ 1 U B | Defined$ Self | Power$ 3 | Toughness$ 2 | Types$ Creature,Elemental | Colors$ Blue,Black | SubAbility$ DBUnblockable | SpellDescription$ CARDNAME becomes a 3/2 blue and black Elemental creature until end of turn and can't be blocked this turn. It's still a land.
|
||||||
|
SVar:DBUnblockable:DB$ Effect | ExileOnMoved$ Battlefield | RememberObjects$ Self | StaticAbilities$ Unblockable
|
||||||
|
SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn.
|
||||||
|
DeckHas:Type$Elemental
|
||||||
Oracle:Creeping Tar Pit enters the battlefield tapped.\n{T}: Add {U} or {B}.\n{1}{U}{B}: Creeping Tar Pit becomes a 3/2 blue and black Elemental creature until end of turn and can't be blocked this turn. It's still a land.
|
Oracle:Creeping Tar Pit enters the battlefield tapped.\n{T}: Add {U} or {B}.\n{1}{U}{B}: Creeping Tar Pit becomes a 3/2 blue and black Elemental creature until end of turn and can't be blocked this turn. It's still a land.
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ ManaCost:1 B
|
|||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
K:Enchant creature card in a graveyard
|
K:Enchant creature card in a graveyard
|
||||||
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
|
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Put enchanted creature card onto the battlefield tapped under your control and attach CARDNAME to it.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Put enchanted creature card onto the battlefield tapped under your control and attach CARDNAME to it. When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||||
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | GainControl$ True | Tapped$ True | SubAbility$ DBAnimate
|
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | GainControl$ True | Tapped$ True | SubAbility$ DBAnimate
|
||||||
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Duration$ Permanent | SubAbility$ DBAttach
|
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Duration$ Permanent | SubAbility$ DBAttach
|
||||||
SVar:DBAttach:DB$ Attach | Defined$ Remembered
|
SVar:DBAttach:DB$ Attach | Defined$ Remembered | SubAbility$ DBDelay
|
||||||
|
SVar:DBDelay:DB$ DelayedTrigger | Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Execute$ TrigSacrifice | RememberObjects$ RememberedLKI | TriggerDescription$ When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||||
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
|
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSacrifice | TriggerDescription$ When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DirectRemembered
|
||||||
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DirectRemembered | SubAbility$ DBCleanup
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player.EnchantedController | TriggerZones$ Battlefield | OptionalDecider$ EnchantedController | Execute$ TrigUntap | TriggerDescription$ At the beginning of the upkeep of enchanted creature's controller, that player may pay {1}{B}. If the player does, untap that creature.
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player.EnchantedController | TriggerZones$ Battlefield | OptionalDecider$ EnchantedController | Execute$ TrigUntap | TriggerDescription$ At the beginning of the upkeep of enchanted creature's controller, that player may pay {1}{B}. If the player does, untap that creature.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:1 W U
|
|||||||
Types:Creature Vedalken Wizard
|
Types:Creature Vedalken Wizard
|
||||||
PT:1/3
|
PT:1/3
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until CARDNAME leaves the battlefield.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until CARDNAME leaves the battlefield.
|
||||||
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | SubAbility$ DBChangeZoneAll | Duration$ UntilHostLeavesPlay
|
SVar:TrigExile:DB$ Pump | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | SubAbility$ DBChangeZoneAll
|
||||||
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Permanent.nonLand+NotDefinedTargeted+sharesNameWith Targeted+ControlledBy TargetedOrController | Duration$ UntilHostLeavesPlay
|
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ TargetedCard.Self,Permanent.nonLand+NotDefinedTargeted+sharesNameWith Targeted+ControlledBy TargetedController | Duration$ UntilHostLeavesPlay
|
||||||
Oracle:When Deputy of Detention enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until Deputy of Detention leaves the battlefield.
|
Oracle:When Deputy of Detention enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until Deputy of Detention leaves the battlefield.
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ Name:Dimir Keyrune
|
|||||||
ManaCost:3
|
ManaCost:3
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
A:AB$ Mana | Cost$ T | Produced$ Combo U B | SpellDescription$ Add {U} or {B}.
|
A:AB$ Mana | Cost$ T | Produced$ Combo U B | SpellDescription$ Add {U} or {B}.
|
||||||
A:AB$ Animate | Cost$ U B | Defined$ Self | Power$ 2 | Toughness$ 2 | Types$ Artifact,Creature,Horror | Colors$ Blue,Black | HiddenKeywords$ Unblockable | SpellDescription$ CARDNAME becomes a 2/2 blue and black Horror artifact creature until end of turn and can't be blocked this turn.
|
A:AB$ Animate | Cost$ U B | Defined$ Self | Power$ 2 | Toughness$ 2 | Types$ Artifact,Creature,Horror | Colors$ Blue,Black | SubAbility$ DBUnblockable | SpellDescription$ CARDNAME becomes a 2/2 blue and black Horror artifact creature until end of turn and can't be blocked this turn.
|
||||||
|
SVar:DBUnblockable:DB$ Effect | ExileOnMoved$ Battlefield | RememberObjects$ Self | StaticAbilities$ Unblockable
|
||||||
|
SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn.
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
DeckHas:Type$Horror
|
||||||
DeckNeeds:Color$Blue|Black
|
DeckNeeds:Color$Blue|Black
|
||||||
Oracle:{T}: Add {U} or {B}.\n{U}{B}: Dimir Keyrune becomes a 2/2 blue and black Horror artifact creature until end of turn and can't be blocked this turn.
|
Oracle:{T}: Add {U} or {B}.\n{U}{B}: Dimir Keyrune becomes a 2/2 blue and black Horror artifact creature until end of turn and can't be blocked this turn.
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ Types:Creature Djinn Pirate
|
|||||||
PT:4/2
|
PT:4/2
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
|
||||||
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
|
||||||
T:Mode$ AttackersDeclared | AttackingPlayer$ Player.Opponent | AttackedTarget$ You | NoResolvingCheck$ True | CheckDefinedPlayer$ You.isMonarch | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever an opponent attacks you while you're the monarch, CARDNAME deals damage to that player equal to the number of cards in their hand.
|
T:Mode$ AttackersDeclared | AttackingPlayer$ Player.Opponent | AttackedTarget$ You | NoResolvingCheck$ True | CheckDefinedPlayer$ You.isMonarch | TriggerZones$ Battlefield | Execute$ TrigDmg | TriggerDescription$ Whenever an opponent attacks you while you're the monarch, CARDNAME deals damage to that player equal to the number of cards in their hand.
|
||||||
SVar:TrigPump:DB$ Pump | RememberObjects$ TriggeredAttackingPlayer | SubAbility$ DBDmg
|
SVar:TrigDmg:DB$ DealDamage | Defined$ TriggeredAttackingPlayer | NumDmg$ X
|
||||||
SVar:DBDmg:DB$ DealDamage | Defined$ Remembered | NumDmg$ X | SubAbility$ DBCleanup
|
SVar:X:Count$ValidHand Card.OwnedBy TriggeredAttackingPlayer
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
|
||||||
SVar:X:Count$ValidHand Card.RememberedPlayerCtrl
|
|
||||||
Oracle:When Emberwilde Captain enters the battlefield, you become the monarch.\nWhenever an opponent attacks you while you're the monarch, Emberwilde Captain deals damage to that player equal to the number of cards in their hand.
|
Oracle:When Emberwilde Captain enters the battlefield, you become the monarch.\nWhenever an opponent attacks you while you're the monarch, Emberwilde Captain deals damage to that player equal to the number of cards in their hand.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ A:SP$ ChangeZone | Cost$ 2 B B | Origin$ Battlefield | Destination$ Exile | Vali
|
|||||||
SVar:ExileYard:DB$ ChangeZoneAll | Origin$ Graveyard | Destination$ Exile | Defined$ RememberedController | ChangeType$ Remembered.sameName | SubAbility$ ExileHand | StackDescription$ None
|
SVar:ExileYard:DB$ ChangeZoneAll | Origin$ Graveyard | Destination$ Exile | Defined$ RememberedController | ChangeType$ Remembered.sameName | SubAbility$ ExileHand | StackDescription$ None
|
||||||
SVar:ExileHand:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ RememberedController | ChangeType$ Remembered.sameName | ChangeNum$ NumInHand | Chooser$ You | SubAbility$ ExileLib | StackDescription$ None
|
SVar:ExileHand:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ RememberedController | ChangeType$ Remembered.sameName | ChangeNum$ NumInHand | Chooser$ You | SubAbility$ ExileLib | StackDescription$ None
|
||||||
SVar:ExileLib:DB$ ChangeZone | Origin$ Library | Destination$ Exile | DefinedPlayer$ RememberedController | ChangeType$ Remembered.sameName | ChangeNum$ NumInLib | Chooser$ You | Search$ True | Shuffle$ True | SubAbility$ DBCleanup | StackDescription$ None
|
SVar:ExileLib:DB$ ChangeZone | Origin$ Library | Destination$ Exile | DefinedPlayer$ RememberedController | ChangeType$ Remembered.sameName | ChangeNum$ NumInLib | Chooser$ You | Search$ True | Shuffle$ True | SubAbility$ DBCleanup | StackDescription$ None
|
||||||
SVar:NumInHand:RememberedController$CardsInHand
|
SVar:NumInHand:PlayerCountRememberedController$CardsInHand
|
||||||
SVar:NumInLib:RememberedController$CardsInLibrary
|
SVar:NumInLib:PlayerCountRememberedController$CardsInLibrary
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
Oracle:Exile target nonblack creature. Search its controller's graveyard, hand, and library for all cards with the same name as that creature and exile them. Then that player shuffles.
|
Oracle:Exile target nonblack creature. Search its controller's graveyard, hand, and library for all cards with the same name as that creature and exile them. Then that player shuffles.
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ Name:Eye of Singularity
|
|||||||
ManaCost:3 W
|
ManaCost:3 W
|
||||||
Types:World Enchantment
|
Types:World Enchantment
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRepeat | TriggerDescription$ When CARDNAME enters the battlefield, destroy each permanent with the same name as another permanent, except for basic lands. They can't be regenerated.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRepeat | TriggerDescription$ When CARDNAME enters the battlefield, destroy each permanent with the same name as another permanent, except for basic lands. They can't be regenerated.
|
||||||
SVar:TrigRepeat:DB$ RepeatEach | RepeatCards$ Permanent.nonBasic | RepeatSubAbility$ DBDestroy | UseImprinted$ True | SubAbility$ DBCleanup
|
SVar:TrigRepeat:DB$ RepeatEach | RepeatCards$ Permanent.nonBasic | RepeatSubAbility$ DBRem | SubAbility$ DBDestroy
|
||||||
SVar:DBDestroy:DB$ Destroy | Defined$ Valid Permanent.sharesNameWith Imprinted+IsNotImprinted | NoRegen$ True
|
SVar:DBRem:DB$ Pump | ImprintCards$ Valid Permanent.sharesNameWith Remembered+IsNotRemembered
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Permanent.nonBasic | TriggerZones$ Battlefield | Execute$ TrigDestroyRem | TriggerDescription$ Whenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated.
|
SVar:DBDestroy:DB$ DestroyAll | ValidCards$ Card.IsImprinted | NoRegen$ True | SubAbility$ DBCleanup
|
||||||
SVar:TrigDestroyRem:DB$ Pump | RememberObjects$ TriggeredCard | SubAbility$ DBDestroyAll
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Permanent.IsNotRemembered+sharesNameWith Remembered | NoRegen$ True | SubAbility$ DBCleanup
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Permanent.nonBasic | TriggerZones$ Battlefield | Execute$ TrigDestroy | TriggerDescription$ Whenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated.
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Permanent.NotTriggeredCard+sharesNameWith TriggeredCard | NoRegen$ True
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
Oracle:When Eye of Singularity enters the battlefield, destroy each permanent with the same name as another permanent, except for basic lands. They can't be regenerated.\nWhenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated.
|
Oracle:When Eye of Singularity enters the battlefield, destroy each permanent with the same name as another permanent, except for basic lands. They can't be regenerated.\nWhenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Enchantment Aura
|
|||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature | AILogic$ Pump
|
A:SP$ Attach | Cost$ 2 W | ValidTgts$ Creature | AILogic$ Pump
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Enchanted creature gets +2/+2.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Enchanted creature gets +2/+2.
|
||||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy Aura.Other | AddKeyword$ First Strike & Lifelink | Description$ As long as another Aura is attached to enchanted creature, it has first strike and lifelink.
|
S:Mode$ Continuous | Affected$ Creature.EnchantedBy+EnchantedBy Aura.Other | AddKeyword$ First Strike & Lifelink | Description$ As long as another Aura is attached to enchanted creature, it has first strike and lifelink.
|
||||||
SVar:EnchantMe:Multiple
|
SVar:EnchantMe:Multiple
|
||||||
Oracle:Enchant creature\nEnchanted creature gets +2/+2.\nAs long as another Aura is attached to enchanted creature, it has first strike and lifelink.
|
Oracle:Enchant creature\nEnchanted creature gets +2/+2.\nAs long as another Aura is attached to enchanted creature, it has first strike and lifelink.
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ PT:3/3
|
|||||||
T:Mode$ ChangesZone | ValidCard$ Artifact.nonToken+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a nontoken artifact you control is put into a graveyard from the battlefield, create a colorless artifact token named Scrap.
|
T:Mode$ ChangesZone | ValidCard$ Artifact.nonToken+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a nontoken artifact you control is put into a graveyard from the battlefield, create a colorless artifact token named Scrap.
|
||||||
SVar:TrigToken:DB$ Token | TokenScript$ scrap
|
SVar:TrigToken:DB$ Token | TokenScript$ scrap
|
||||||
A:AB$ Charm | Cost$ 1 R Sac<1/Artifact> | Choices$ DBCounter,DBGoad,DBLoot | CharmNum$ 1
|
A:AB$ Charm | Cost$ 1 R Sac<1/Artifact> | Choices$ DBCounter,DBGoad,DBLoot | CharmNum$ 1
|
||||||
SVar:DBCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on NICKNAME. It gains haste until end of turn.
|
SVar:DBCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on NICKNAME. It gains menace until end of turn.
|
||||||
SVar:DBPump:DB$ Pump | Defined$ Self | KW$ Haste
|
SVar:DBPump:DB$ Pump | Defined$ Self | KW$ Menace
|
||||||
SVar:DBGoad:DB$ Goad | ValidTgts$ Creature | SpellDescription$ Goad target creature.
|
SVar:DBGoad:DB$ Goad | ValidTgts$ Creature | SpellDescription$ Goad target creature.
|
||||||
SVar:DBLoot:DB$ Discard | Mode$ TgtChoose | SubAbility$ DBDraw | SpellDescription$ Discard a card, then draw a card.
|
SVar:DBLoot:DB$ Discard | Mode$ TgtChoose | SubAbility$ DBDraw | SpellDescription$ Discard a card, then draw a card.
|
||||||
SVar:DBDraw:DB$ Draw
|
SVar:DBDraw:DB$ Draw
|
||||||
SVar:AIPreference:SacCost$Artifact.token
|
SVar:AIPreference:SacCost$Artifact.token
|
||||||
DeckHints:Type$Artifact
|
DeckHints:Type$Artifact
|
||||||
DeckHas:Ability$Discard|Token|Counters & Type$Artifact & Keyword$Haste
|
DeckHas:Ability$Discard|Token|Counters & Type$Artifact & Keyword$Menace
|
||||||
Oracle:Whenever a nontoken artifact you control is put into a graveyard from the battlefield, create a colorless artifact token named Scrap.\n{1}{R}, Sacrifice an artifact: Choose one —\n• Put a +1/+1 counter on Farid. It gains haste until end of turn.\n• Goad target creature.\n• Discard a card, then draw a card.
|
Oracle:Whenever a nontoken artifact you control is put into a graveyard from the battlefield, create a colorless artifact token named Scrap.\n{1}{R}, Sacrifice an artifact: Choose one —\n• Put a +1/+1 counter on Farid. It gains menace until end of turn.\n• Goad target creature.\n• Discard a card, then draw a card.
|
||||||
@@ -2,7 +2,7 @@ Name:Gorilla Tactics
|
|||||||
ManaCost:1 G
|
ManaCost:1 G
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Token | Cost$ 1 G | TokenScript$ g_2_2_gorilla | SpellDescription$ Create a 2/2 green Gorilla creature token.
|
A:SP$ Token | Cost$ 1 G | TokenScript$ g_2_2_gorilla | SpellDescription$ Create a 2/2 green Gorilla creature token.
|
||||||
T:Mode$ Discarded | ValidCard$ Card.Self | ValidCause$ Card.OppCtrl | Execute$ TrigDouble | TriggerDescription$ When a spell or ability an opponent controls causes you to discard Gorilla Tactics, create two 2/2 green Gorilla creature tokens.
|
T:Mode$ Discarded | ValidCard$ Card.Self | ValidCause$ Card.OppCtrl | Execute$ TrigDouble | TriggerDescription$ When a spell or ability an opponent controls causes you to discard CARDNAME, create two 2/2 green Gorilla creature tokens.
|
||||||
SVar:TrigDouble:DB$ Token | TokenScript$ g_2_2_gorilla | TokenAmount$ 2
|
SVar:TrigDouble:DB$ Token | TokenScript$ g_2_2_gorilla | TokenAmount$ 2
|
||||||
DeckHas:Ability$Token
|
DeckHas:Ability$Token
|
||||||
Oracle:Create a 2/2 green Gorilla creature token.\nWhen a spell or ability an opponent controls causes you to discard Gorilla Tactics, create two 2/2 green Gorilla creature tokens.
|
Oracle:Create a 2/2 green Gorilla creature token.\nWhen a spell or ability an opponent controls causes you to discard Gorilla Tactics, create two 2/2 green Gorilla creature tokens.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Grafdigger's Cage
|
Name:Grafdigger's Cage
|
||||||
ManaCost:1
|
ManaCost:1
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards and libraries can't enter the battlefield.
|
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards and libraries can't enter the battlefield.
|
||||||
S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
|
S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ ManaCost:1 W B
|
|||||||
Types:Legendary Creature Rat Pilot
|
Types:Legendary Creature Rat Pilot
|
||||||
PT:4/3
|
PT:4/3
|
||||||
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.
|
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | AnimateSubAbility$ Animate
|
SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | SubAbility$ Animate | RememberChanged$ True | AtEOT$ Hand
|
||||||
SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | AtEOT$ Hand
|
SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | SubAbility$ DBCleanup
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
DeckHas:Ability$Graveyard
|
DeckHas:Ability$Graveyard
|
||||||
DeckNeeds:Type$Vehicle
|
DeckNeeds:Type$Vehicle
|
||||||
Oracle:At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.
|
Oracle:At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:1 B B
|
|||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Charm | Choices$ DBDraw,DBDebuff
|
A:SP$ Charm | Choices$ DBDraw,DBDebuff
|
||||||
SVar:DBDraw:DB$ Draw | NumCards$ 2 | SubAbility$ DBLoseLife | SpellDescription$ You draw two cards and you lose 2 life.
|
SVar:DBDraw:DB$ Draw | NumCards$ 2 | SubAbility$ DBLoseLife | SpellDescription$ You draw two cards and you lose 2 life.
|
||||||
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1
|
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2
|
||||||
SVar:DBDebuff:DB$ PumpAll | ValidCards$ Creature.OppCtrl | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Creatures your opponents control get -1/-1 until end of turn.
|
SVar:DBDebuff:DB$ PumpAll | ValidCards$ Creature.OppCtrl | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Creatures your opponents control get -1/-1 until end of turn.
|
||||||
Oracle:Choose one —\n• You draw two cards and you lose 2 life.\n• Creatures your opponents control get -1/-1 until end of turn.
|
Oracle:Choose one —\n• You draw two cards and you lose 2 life.\n• Creatures your opponents control get -1/-1 until end of turn.
|
||||||
@@ -7,7 +7,7 @@ SVar:SacMe:3
|
|||||||
T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw your second card each turn, put a +1/+1 counter on CARDNAME.
|
T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw your second card each turn, put a +1/+1 counter on CARDNAME.
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
|
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME dies, return another target creature card with mana value less than or equal to CARDNAME's power from your graveyard to the battlefield.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME dies, return another target creature card with mana value less than or equal to CARDNAME's power from your graveyard to the battlefield.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | ValidTgt$ Creature.YouOwn+cmcLEX+Other | TgtPrompt$ Choose another target creature card with mana value less than or equal to CARDNAME's power | Origin$ Graveyard | Destination$ Battlefield
|
SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.YouOwn+cmcLEX+Other | TgtPrompt$ Choose another target creature card with mana value less than or equal to CARDNAME's power | Origin$ Graveyard | Destination$ Battlefield
|
||||||
SVar:X:TriggeredCard$CardPower
|
SVar:X:TriggeredCard$CardPower
|
||||||
DeckHas:Ability$Counters|Graveyard
|
DeckHas:Ability$Counters|Graveyard
|
||||||
Oracle:Flying\nWhenever you draw your second card each turn, put a +1/+1 counter on Gurgling Anointer.\nWhen Gurgling Anointer dies, return another target creature card with mana value less than or equal to Gurgling Anointer's power from your graveyard to the battlefield.
|
Oracle:Flying\nWhenever you draw your second card each turn, put a +1/+1 counter on Gurgling Anointer.\nWhen Gurgling Anointer dies, return another target creature card with mana value less than or equal to Gurgling Anointer's power from your graveyard to the battlefield.
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ K:Mutate:3 RG U U
|
|||||||
K:Flying
|
K:Flying
|
||||||
K:Trample
|
K:Trample
|
||||||
T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigDigUntil | TriggerDescription$ Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.
|
T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigDigUntil | TriggerDescription$ Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.
|
||||||
SVar:TrigDigUntil:DB$ DigUntil | Valid$ Permanent.nonLand | ValidDescription$ nonland permanent | FoundDestination$ Exile | RevealedDestination$ Exile | RememberFound$ True | SubAbility$ DBChoose
|
SVar:TrigDigUntil:DB$ DigUntil | Valid$ Permanent.nonLand | ValidDescription$ nonland permanent | FoundDestination$ Exile | RevealedDestination$ Exile | RememberFound$ True | SubAbility$ DBChange
|
||||||
SVar:DBChoose:DB$ GenericChoice | Choices$ Battlefield,Hand | Defined$ You
|
SVar:DBChange:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | DestinationAlternative$ Hand | AlternativeDestinationMessage$ Put that card onto the battlefield instead of putting it into your hand? | SubAbility$ DBCleanup
|
||||||
SVar:Battlefield:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup | SpellDescription$ Put the nonland permanent onto the battlefield
|
|
||||||
SVar:Hand:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand | SubAbility$ DBCleanup | SpellDescription$ Put the nonland permanent into your hand
|
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
Oracle:Mutate {3}{R/G}{U}{U} (If you cast this spell for its mutate cost, put it over or under target non-Human creature you own. They mutate into the creature on top plus all abilities from under it.)\nFlying, trample\nWhenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.
|
Oracle:Mutate {3}{R/G}{U}{U} (If you cast this spell for its mutate cost, put it over or under target non-Human creature you own. They mutate into the creature on top plus all abilities from under it.)\nFlying, trample\nWhenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ Types:Creature Human Scout
|
|||||||
PT:3/1
|
PT:3/1
|
||||||
K:Lifelink
|
K:Lifelink
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {1}{W} any number of times. When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {1}{W} any number of times. When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
||||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<1 W\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<1 W\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | RememberSVarAmount$ NumTimes | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ VALOR | CounterNum$ NumTimes
|
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ VALOR | CounterNum$ X
|
||||||
SVar:NumTimes:Number$0
|
SVar:X:Count$TriggerRememberAmount
|
||||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ Z | AddToughness$ Z | Description$ Creatures you control get +1/+1 for each valor counter on CARDNAME.
|
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ Z | AddToughness$ Z | Description$ Creatures you control get +1/+1 for each valor counter on CARDNAME.
|
||||||
SVar:Z:Count$CardCounters.VALOR
|
SVar:Z:Count$CardCounters.VALOR
|
||||||
DeckHas:Ability$LifeGain|Counters
|
DeckHas:Ability$LifeGain|Counters
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ PT:3/3
|
|||||||
K:Vigilance
|
K:Vigilance
|
||||||
K:Menace
|
K:Menace
|
||||||
K:Lifelink
|
K:Lifelink
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards can't enter the battlefield.
|
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards can't enter the battlefield.
|
||||||
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards.
|
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards.
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards.
|
Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
Name:Legions to Ashes
|
Name:Legions to Ashes
|
||||||
ManaCost:1 W B
|
ManaCost:1 W B
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ ChangeZoneAll | TgtPrompt$ Select target nonland permanent an opponent controls | ValidTgts$ Permanent.nonLand+OppCtrl | ChangeType$ TargetedCard.Self,Creature.NotDefinedTargeted+token+sharesNameWith Targeted | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target nonland permanent an opponent controls and all tokens that player controls with the same name as that permanent.
|
A:SP$ Pump | ValidTgts$ Permanent.nonland | TgtPrompt$ Select target nonland permanent an opponent controls | SubAbility$ ExileAll | SpellDescription$ Exile target nonland permanent an opponent controls and all tokens that player controls with the same name as that permanent.
|
||||||
|
SVar:ExileAll:DB$ ChangeZoneAll | ChangeType$ TargetedCard.Self,Card.NotDefinedTargeted+token+sharesNameWith Targeted+ControlledBy TargetedController | Origin$ Battlefield | Destination$ Exile
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Exile target nonland permanent an opponent controls and all tokens that player controls with the same name as that permanent.
|
Oracle:Exile target nonland permanent an opponent controls and all tokens that player controls with the same name as that permanent.
|
||||||
@@ -3,7 +3,7 @@ ManaCost:1 W
|
|||||||
Types:Legendary Creature Human Knight
|
Types:Legendary Creature Human Knight
|
||||||
PT:2/2
|
PT:2/2
|
||||||
A:AB$ ChangeZone | Cost$ 1 W | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | Optional$ True | DefinedPlayer$ TargetedController | Chooser$ TargetedController | Origin$ Battlefield | Destination$ Exile | WithCountersType$ AEGIS | StackDescription$ {p:You} chooses {c:Targeted}. {p:TargetedController} may exile it with an aegis counter on it. | SpellDescription$ Choose another target creature. Its controller may exile it with an aegis counter on it.
|
A:AB$ ChangeZone | Cost$ 1 W | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | Optional$ True | DefinedPlayer$ TargetedController | Chooser$ TargetedController | Origin$ Battlefield | Destination$ Exile | WithCountersType$ AEGIS | StackDescription$ {p:You} chooses {c:Targeted}. {p:TargetedController} may exile it with an aegis counter on it. | SpellDescription$ Choose another target creature. Its controller may exile it with an aegis counter on it.
|
||||||
A:AB$ ChangeZoneAll | Cost$ 2 W T | ChangeType$ Creature.counters_GE1_AEGIS | Origin$ Exile | Destination$ Battlefield | SpellDescription$ Return all exiled cards with aegis counters on them to the battlefield under their owners' control.
|
A:AB$ ChangeZoneAll | Cost$ 2 W T | ChangeType$ Card.counters_GE1_AEGIS | Origin$ Exile | Destination$ Battlefield | SpellDescription$ Return all exiled cards with aegis counters on them to the battlefield under their owners' control.
|
||||||
K:Partner
|
K:Partner
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
Oracle:{1}{W}: Choose another target creature. Its controller may exile it with an aegis counter on it.\n{2}{W}, {T}: Return all exiled cards with aegis counters on them to the battlefield under their owners' control.\nPartner (You can have two commanders if both have partner.)
|
Oracle:{1}{W}: Choose another target creature. Its controller may exile it with an aegis counter on it.\n{2}{W}, {T}: Return all exiled cards with aegis counters on them to the battlefield under their owners' control.\nPartner (You can have two commanders if both have partner.)
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Graveyard | ValidCard$ Creature
|
|||||||
SVar:TrigReanimate:AB$ ChangeZone | Cost$ PayLife<X> | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | ChangeNum$ 1 | AnimateSubAbility$ Animate
|
SVar:TrigReanimate:AB$ ChangeZone | Cost$ PayLife<X> | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | ChangeNum$ 1 | AnimateSubAbility$ Animate
|
||||||
SVar:Animate:DB$ Animate | Defined$ TriggeredCard | Types$ Warlock | Duration$ Permanent
|
SVar:Animate:DB$ Animate | Defined$ TriggeredCard | Types$ Warlock | Duration$ Permanent
|
||||||
SVar:X:TriggeredCard$CardManaCost
|
SVar:X:TriggeredCard$CardManaCost
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidLKI$ Creature.Warlock+YouCtrl | ReplaceWith$ Exile | CheckSelfLKIZone$ True | Description$ If a Warlock you control would die, exile it instead.
|
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidLKI$ Warlock.YouCtrl | ReplaceWith$ Exile | CheckSelfLKIZone$ True | Description$ If a Warlock you control would die, exile it instead.
|
||||||
SVar:Exile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ ReplacedCard
|
SVar:Exile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ ReplacedCard
|
||||||
Oracle:Flying\nWhenever a creature card is put into an opponent's graveyard from anywhere, you may pay life equal to its mana value. If you do, put it onto the battlefield under your control. It's a Warlock in addition to its other types.\nIf a Warlock you control would die, exile it instead.
|
Oracle:Flying\nWhenever a creature card is put into an opponent's graveyard from anywhere, you may pay life equal to its mana value. If you do, put it onto the battlefield under your control. It's a Warlock in addition to its other types.\nIf a Warlock you control would die, exile it instead.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:2 W
|
|||||||
Types:Creature Human Soldier
|
Types:Creature Human Soldier
|
||||||
PT:2/2
|
PT:2/2
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.powerLE2+YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigDraw | Optional$ True | TriggerDescription$ Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.powerLE2+YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigDraw | Optional$ True | TriggerDescription$ Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.
|
||||||
SVar:TrigDraw:AB$Draw | Cost$ 1 | NumCards$ 1 | SpellDescription$ Draw a card.
|
SVar:TrigDraw:AB$ Draw | Cost$ 1 | NumCards$ 1 | SpellDescription$ Draw a card.
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
Oracle:Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.
|
Oracle:Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:3 U
|
|||||||
Types:Creature Faerie
|
Types:Creature Faerie
|
||||||
PT:2/2
|
PT:2/2
|
||||||
K:Flying
|
K:Flying
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw cards equal to the number of opponents who were dealt combat damage this turn.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw cards equal to the number of opponents who were dealt combat damage this turn.
|
||||||
SVar:TrigDraw:DB$ Draw | NumCards$ X
|
SVar:TrigDraw:DB$ Draw | NumCards$ X
|
||||||
SVar:X:PlayerCountRegisteredOpponents$HasPropertywasDealtCombatDamageThisTurn
|
SVar:X:PlayerCountRegisteredOpponents$HasPropertywasDealtCombatDamageThisTurn
|
||||||
AlternateMode:Adventure
|
AlternateMode:Adventure
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:1 B
|
|||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ RollDice | Sides$ 20 | ResultSubAbilities$ 1-9:OneOppSac,10-19:EachOppSac,20:SacTopPower | SpellDescription$ Roll a d20.
|
A:SP$ RollDice | Sides$ 20 | ResultSubAbilities$ 1-9:OneOppSac,10-19:EachOppSac,20:SacTopPower | SpellDescription$ Roll a d20.
|
||||||
SVar:OneOppSac:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | SubAbility$ DBSac | StackDescription$ SpellDescription | SpellDescription$ 1—9 VERT Choose an opponent. That player sacrifices a creature.
|
SVar:OneOppSac:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | SubAbility$ DBSac | StackDescription$ SpellDescription | SpellDescription$ 1—9 VERT Choose an opponent. That player sacrifices a creature.
|
||||||
SVar:DBSac:DB$ Sacrifice | Defined$ Chosen | SacValid$ Creature | SubAbility$ DBCleanupChosen
|
SVar:DBSac:DB$ Sacrifice | Defined$ ChosenPlayer | SacValid$ Creature | SubAbility$ DBCleanupChosen
|
||||||
SVar:EachOppSac:DB$ Sacrifice | Defined$ Player.Opponent | SacValid$ Creature | StackDescription$ SpellDescription | SpellDescription$ 10—19 VERT Each opponent sacrifices a creature.
|
SVar:EachOppSac:DB$ Sacrifice | Defined$ Player.Opponent | SacValid$ Creature | StackDescription$ SpellDescription | SpellDescription$ 10—19 VERT Each opponent sacrifices a creature.
|
||||||
SVar:SacTopPower:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ DBChooseCard | SubAbility$ DBSacAll | StackDescription$ SpellDescription | SpellDescription$ 20 VERT Each opponent sacrifices a creature with the greatest power among creatures that player controls.
|
SVar:SacTopPower:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ DBChooseCard | SubAbility$ DBSacAll | StackDescription$ SpellDescription | SpellDescription$ 20 VERT Each opponent sacrifices a creature with the greatest power among creatures that player controls.
|
||||||
SVar:DBChooseCard:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Creature.greatestPowerControlledByRemembered | ChoiceTitle$ Choose a creature you control with the greatest power | Mandatory$ True | RememberChosen$ True
|
SVar:DBChooseCard:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Creature.greatestPowerControlledByRemembered | ChoiceTitle$ Choose a creature you control with the greatest power | Mandatory$ True | RememberChosen$ True
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
Name:Nature's Will
|
Name:Nature's Will
|
||||||
ManaCost:2 G G
|
ManaCost:2 G G
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ TrigRememberTarget | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control.
|
T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ DBTapAll | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control.
|
||||||
SVar:TrigRememberTarget:DB$ Pump | RememberObjects$ TriggeredTarget | SubAbility$ DBTapAll
|
SVar:DBTapAll:DB$ TapAll | ValidCards$ Land.ControlledBy TriggeredTarget | SubAbility$ DBUntapAll
|
||||||
SVar:DBTapAll:DB$ TapAll | ValidCards$ Land.RememberedPlayerCtrl | SubAbility$ DBUntapAll
|
SVar:DBUntapAll:DB$ UntapAll | ValidCards$ Land.YouCtrl
|
||||||
SVar:DBUntapAll:DB$ UntapAll | ValidCards$ Land.YouCtrl | SubAbility$ DBCleanup
|
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
Oracle:Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control.
|
Oracle:Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user