mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
ChangesZoneAll: add LKI support (#4385)
* Fix Skeleton Crew triggering from own ETB * Fix bad logic * Add LKI support * Prowl fix when card gets cast from another zone again * Clean up * Clean triggers * Support LKI for leaves graveyard * Update effects * Fix logic * Fix Living Breakthrough for X spells * Extract lastState fallback logic
This commit is contained in:
@@ -932,7 +932,7 @@ public class AiAttackController {
|
|||||||
// check defenders in order of maximum requirements
|
// check defenders in order of maximum requirements
|
||||||
List<Pair<GameEntity, Integer>> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements();
|
List<Pair<GameEntity, Integer>> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements();
|
||||||
final GameEntity def = defender;
|
final GameEntity def = defender;
|
||||||
Collections.sort(reqs, new Comparator<Pair<GameEntity, Integer>>() {
|
reqs.sort(new Comparator<Pair<GameEntity, Integer>>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Pair<GameEntity, Integer> r1, Pair<GameEntity, Integer> r2) {
|
public int compare(Pair<GameEntity, Integer> r1, Pair<GameEntity, Integer> r2) {
|
||||||
if (r1.getValue() == r2.getValue()) {
|
if (r1.getValue() == r2.getValue()) {
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public class AiBlockController {
|
|||||||
ComputerUtilCard.sortByEvaluateCreature(attackers);
|
ComputerUtilCard.sortByEvaluateCreature(attackers);
|
||||||
CardLists.sortByPowerDesc(attackers);
|
CardLists.sortByPowerDesc(attackers);
|
||||||
//move cards like Phage the Untouchable to the front
|
//move cards like Phage the Untouchable to the front
|
||||||
Collections.sort(attackers, new Comparator<Card>() {
|
attackers.sort(new Comparator<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final Card o1, final Card o2) {
|
public int compare(final Card o1, final Card o2) {
|
||||||
if (o1.hasSVar("MustBeBlocked") && !o2.hasSVar("MustBeBlocked")) {
|
if (o1.hasSVar("MustBeBlocked") && !o2.hasSVar("MustBeBlocked")) {
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
protected int removeCounter(GameEntityCounterTable table, List<Card> prefs, CounterEnumType cType, int stillToRemove) {
|
protected int removeCounter(GameEntityCounterTable table, List<Card> prefs, CounterEnumType cType, int stillToRemove) {
|
||||||
int removed = 0;
|
int removed = 0;
|
||||||
if (!prefs.isEmpty() && stillToRemove > 0) {
|
if (!prefs.isEmpty() && stillToRemove > 0) {
|
||||||
Collections.sort(prefs, CardPredicates.compareByCounterType(cType));
|
prefs.sort(CardPredicates.compareByCounterType(cType));
|
||||||
|
|
||||||
for (Card prefCard : prefs) {
|
for (Card prefCard : prefs) {
|
||||||
// already enough removed
|
// already enough removed
|
||||||
@@ -667,7 +667,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
return crd.getCounters(CounterEnumType.QUEST) > e;
|
return crd.getCounters(CounterEnumType.QUEST) > e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));
|
prefs.sort(Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));
|
||||||
|
|
||||||
for (final Card crd : prefs) {
|
for (final Card crd : prefs) {
|
||||||
int e = 0;
|
int e = 0;
|
||||||
|
|||||||
@@ -2451,7 +2451,7 @@ public class ComputerUtil {
|
|||||||
return goodChoices;
|
return goodChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(goodChoices, CardLists.TextLenComparator);
|
goodChoices.sort(CardLists.TextLenComparator);
|
||||||
|
|
||||||
CardLists.sortByCmcDesc(goodChoices);
|
CardLists.sortByCmcDesc(goodChoices);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -402,7 +401,7 @@ public class ComputerUtilAbility {
|
|||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
// TODO this doesn't account for nearly identical creatures where one is a newer but more cost efficient variant
|
// TODO this doesn't account for nearly identical creatures where one is a newer but more cost efficient variant
|
||||||
Collections.sort(creatures, ComputerUtilCard.EvaluateCreatureSpellComparator);
|
creatures.sort(ComputerUtilCard.EvaluateCreatureSpellComparator);
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (int i = 0; i < all.size(); i++) {
|
for (int i = 0; i < all.size(); i++) {
|
||||||
if (all.get(i).getApi() == ApiType.PermanentCreature) {
|
if (all.get(i).getApi() == ApiType.PermanentCreature) {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class ComputerUtilCard {
|
|||||||
* @param list
|
* @param list
|
||||||
*/
|
*/
|
||||||
public static void sortByEvaluateCreature(final CardCollection list) {
|
public static void sortByEvaluateCreature(final CardCollection list) {
|
||||||
Collections.sort(list, ComputerUtilCard.EvaluateCreatureComparator);
|
list.sort(ComputerUtilCard.EvaluateCreatureComparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The AI doesn't really pick the best artifact, just the most expensive.
|
// The AI doesn't really pick the best artifact, just the most expensive.
|
||||||
@@ -552,12 +552,13 @@ public class ComputerUtilCard {
|
|||||||
if (!Iterables.isEmpty(list)) {
|
if (!Iterables.isEmpty(list)) {
|
||||||
CardCollection cc = CardLists.filter(list,
|
CardCollection cc = CardLists.filter(list,
|
||||||
Predicates.or(CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
|
Predicates.or(CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
|
||||||
Collections.sort(cc, CardLists.CmcComparatorInv);
|
|
||||||
|
|
||||||
if (cc.isEmpty()) {
|
if (cc.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cc.sort(CardLists.CmcComparatorInv);
|
||||||
|
|
||||||
Card cheapest = cc.getLast();
|
Card cheapest = cc.getLast();
|
||||||
if (cheapest.hasSVar("DoNotDiscardIfAble")) {
|
if (cheapest.hasSVar("DoNotDiscardIfAble")) {
|
||||||
for (int i = cc.size() - 1; i >= 0; i--) {
|
for (int i = cc.size() - 1; i >= 0; i--) {
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(orderedCards, new Comparator<Card>() {
|
orderedCards.sort(new Comparator<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final Card card1, final Card card2) {
|
public int compare(final Card card1, final Card card2) {
|
||||||
return Integer.compare(manaCardMap.get(card1), manaCardMap.get(card2));
|
return Integer.compare(manaCardMap.get(card1), manaCardMap.get(card2));
|
||||||
@@ -149,7 +149,7 @@ public class ComputerUtilMana {
|
|||||||
System.out.println("Unsorted Abilities: " + newAbilities);
|
System.out.println("Unsorted Abilities: " + newAbilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(newAbilities, new Comparator<SpellAbility>() {
|
newAbilities.sort(new Comparator<SpellAbility>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||||
int preOrder = orderedCards.indexOf(ability1.getHostCard()) - orderedCards.indexOf(ability2.getHostCard());
|
int preOrder = orderedCards.indexOf(ability1.getHostCard()) - orderedCards.indexOf(ability2.getHostCard());
|
||||||
@@ -195,7 +195,7 @@ public class ComputerUtilMana {
|
|||||||
final List<SpellAbility> prefSortedAbilities = new ArrayList<>(newAbilities);
|
final List<SpellAbility> prefSortedAbilities = new ArrayList<>(newAbilities);
|
||||||
final List<SpellAbility> otherSortedAbilities = new ArrayList<>(newAbilities);
|
final List<SpellAbility> otherSortedAbilities = new ArrayList<>(newAbilities);
|
||||||
|
|
||||||
Collections.sort(prefSortedAbilities, new Comparator<SpellAbility>() {
|
prefSortedAbilities.sort(new Comparator<SpellAbility>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||||
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
||||||
@@ -206,7 +206,7 @@ public class ComputerUtilMana {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Collections.sort(otherSortedAbilities, new Comparator<SpellAbility>() {
|
otherSortedAbilities.sort(new Comparator<SpellAbility>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||||
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
||||||
@@ -1094,7 +1094,7 @@ public class ComputerUtilMana {
|
|||||||
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) {
|
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) {
|
||||||
List<ManaCostShard> shardsToPay = Lists.newArrayList(cost.getDistinctShards());
|
List<ManaCostShard> shardsToPay = Lists.newArrayList(cost.getDistinctShards());
|
||||||
// optimize order so that the shards with less available sources are considered first
|
// optimize order so that the shards with less available sources are considered first
|
||||||
Collections.sort(shardsToPay, new Comparator<ManaCostShard>() {
|
shardsToPay.sort(new Comparator<ManaCostShard>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(final ManaCostShard shard1, final ManaCostShard shard2) {
|
public int compare(final ManaCostShard shard1, final ManaCostShard shard2) {
|
||||||
return sourcesForShards.get(shard1).size() - sourcesForShards.get(shard2).size();
|
return sourcesForShards.get(shard1).size() - sourcesForShards.get(shard2).size();
|
||||||
|
|||||||
@@ -1398,7 +1398,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
|
chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
|
||||||
}
|
}
|
||||||
if (MyRandom.percentTrue(chance)) {
|
if (MyRandom.percentTrue(chance)) {
|
||||||
Collections.sort(aiPlaneswalkers, CardPredicates.compareByCounterType(CounterEnumType.LOYALTY));
|
aiPlaneswalkers.sort(CardPredicates.compareByCounterType(CounterEnumType.LOYALTY));
|
||||||
for (Card pw : aiPlaneswalkers) {
|
for (Card pw : aiPlaneswalkers) {
|
||||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||||
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
|
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
|
|
||||||
public String toCardList(String separator) {
|
public String toCardList(String separator) {
|
||||||
List<Entry<PaperCard, Integer>> main2sort = Lists.newArrayList(this);
|
List<Entry<PaperCard, Integer>> main2sort = Lists.newArrayList(this);
|
||||||
Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET);
|
main2sort.sort(ItemPoolSorter.BY_NAME_THEN_SET);
|
||||||
final CardDb commonDb = StaticData.instance().getCommonCards();
|
final CardDb commonDb = StaticData.instance().getCommonCards();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
|||||||
@@ -794,7 +794,7 @@ public class Game {
|
|||||||
boolean planarControllerLost = false;
|
boolean planarControllerLost = false;
|
||||||
boolean planarOwnerLost = false;
|
boolean planarOwnerLost = false;
|
||||||
boolean isMultiplayer = getPlayers().size() > 2;
|
boolean isMultiplayer = getPlayers().size() > 2;
|
||||||
CardZoneTable triggerList = new CardZoneTable();
|
CardZoneTable triggerList = new CardZoneTable(getLastStateBattlefield(), getLastStateGraveyard());
|
||||||
|
|
||||||
// 702.142f & 707.9
|
// 702.142f & 707.9
|
||||||
// If a player leaves the game, all face-down cards that player owns must be revealed to all players.
|
// If a player leaves the game, all face-down cards that player owns must be revealed to all players.
|
||||||
|
|||||||
@@ -184,24 +184,8 @@ public class GameAction {
|
|||||||
lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI);
|
lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CardCollectionView lastBattlefield = null;
|
CardCollectionView lastBattlefield = getLastState(AbilityKey.LastStateBattlefield, cause, params);
|
||||||
CardCollectionView lastGraveyard = null;
|
CardCollectionView lastGraveyard = getLastState(AbilityKey.LastStateGraveyard, cause, params);
|
||||||
if (params != null) {
|
|
||||||
lastBattlefield = (CardCollectionView) params.get(AbilityKey.LastStateBattlefield);
|
|
||||||
lastGraveyard = (CardCollectionView) params.get(AbilityKey.LastStateGraveyard);
|
|
||||||
}
|
|
||||||
if (lastBattlefield == null && cause != null) {
|
|
||||||
lastBattlefield = cause.getLastStateBattlefield();
|
|
||||||
}
|
|
||||||
if (lastGraveyard == null && cause != null) {
|
|
||||||
lastGraveyard = cause.getLastStateGraveyard();
|
|
||||||
}
|
|
||||||
if (lastBattlefield == null) {
|
|
||||||
lastBattlefield = game.getLastStateBattlefield();
|
|
||||||
}
|
|
||||||
if (lastGraveyard == null) {
|
|
||||||
lastGraveyard = game.getLastStateGraveyard();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.isSplitCard()) {
|
if (c.isSplitCard()) {
|
||||||
boolean resetToOriginal = false;
|
boolean resetToOriginal = false;
|
||||||
@@ -211,11 +195,9 @@ public class GameAction {
|
|||||||
// Make sure the card returns from the battlefield as the original card with two halves
|
// Make sure the card returns from the battlefield as the original card with two halves
|
||||||
resetToOriginal = true;
|
resetToOriginal = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!zoneTo.is(ZoneType.Stack)) {
|
||||||
if (!zoneTo.is(ZoneType.Stack)) {
|
// For regular splits, recreate the original state unless the card is going to stack as one half
|
||||||
// For regular splits, recreate the original state unless the card is going to stack as one half
|
resetToOriginal = true;
|
||||||
resetToOriginal = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resetToOriginal) {
|
if (resetToOriginal) {
|
||||||
@@ -917,7 +899,7 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final CardCollection exile(final CardCollection cards, SpellAbility cause, Map<AbilityKey, Object> params) {
|
public final CardCollection exile(final CardCollection cards, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable(getLastState(AbilityKey.LastStateBattlefield, cause, params), getLastState(AbilityKey.LastStateGraveyard, cause, params));
|
||||||
CardCollection result = new CardCollection();
|
CardCollection result = new CardCollection();
|
||||||
for (Card card : cards) {
|
for (Card card : cards) {
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
@@ -964,7 +946,6 @@ public class GameAction {
|
|||||||
if (z.is(ZoneType.Battlefield)) {
|
if (z.is(ZoneType.Battlefield)) {
|
||||||
c.runLeavesPlayCommands();
|
c.runLeavesPlayCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CR 603.6c other players LTB triggers should work
|
// CR 603.6c other players LTB triggers should work
|
||||||
@@ -1042,7 +1023,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily disable (if mode = true) actively checking static abilities.
|
// Temporarily disable (if mode = true) actively checking static abilities.
|
||||||
@@ -1264,7 +1244,7 @@ public class GameAction {
|
|||||||
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
|
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
|
||||||
boolean checkAgain = false;
|
boolean checkAgain = false;
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
|
|
||||||
for (final Player p : game.getPlayers()) {
|
for (final Player p : game.getPlayers()) {
|
||||||
for (final ZoneType zt : ZoneType.values()) {
|
for (final ZoneType zt : ZoneType.values()) {
|
||||||
@@ -2593,4 +2573,28 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CardCollectionView getLastState(final AbilityKey key, final SpellAbility cause, final Map<AbilityKey, Object> params) {
|
||||||
|
CardCollectionView lastState = null;
|
||||||
|
if (params != null) {
|
||||||
|
lastState = (CardCollectionView) params.get(key);
|
||||||
|
}
|
||||||
|
if (lastState == null && cause != null) {
|
||||||
|
if (key == AbilityKey.LastStateBattlefield) {
|
||||||
|
lastState = cause.getLastStateBattlefield();
|
||||||
|
}
|
||||||
|
if (key == AbilityKey.LastStateGraveyard) {
|
||||||
|
lastState = cause.getLastStateGraveyard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastState == null) {
|
||||||
|
if (key == AbilityKey.LastStateBattlefield) {
|
||||||
|
lastState = game.getLastStateBattlefield();
|
||||||
|
}
|
||||||
|
if (key == AbilityKey.LastStateGraveyard) {
|
||||||
|
lastState = game.getLastStateGraveyard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2141,9 +2141,11 @@ public class AbilityUtils {
|
|||||||
if (sq[0].contains("CardManaCost")) {
|
if (sq[0].contains("CardManaCost")) {
|
||||||
int cmc = c.getCMC();
|
int cmc = c.getCMC();
|
||||||
|
|
||||||
if (sq[0].contains("LKI") && ctb instanceof SpellAbility && !c.isInZone(ZoneType.Stack) && c.getManaCost() != null) {
|
if (sq[0].contains("LKI") && !c.isInZone(ZoneType.Stack) && c.getManaCost() != null) {
|
||||||
if (((SpellAbility) ctb).getXManaCostPaid() != null) {
|
if (ctb instanceof SpellAbility && ((SpellAbility) ctb).getXManaCostPaid() != null) {
|
||||||
cmc += ((SpellAbility) ctb).getXManaCostPaid() * c.getManaCost().countX();
|
cmc += ((SpellAbility) ctb).getXManaCostPaid() * c.getManaCost().countX();
|
||||||
|
} else {
|
||||||
|
cmc += c.getXManaCostPaid() * c.getManaCost().countX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ public class AmassEffect extends TokenEffectBase {
|
|||||||
makeTokenTable(makeTokenTableInternal(activator, result, 1), false, triggerList, combatChanged, sa);
|
makeTokenTable(makeTokenTableInternal(activator, result, 1), false, triggerList, combatChanged, sa);
|
||||||
|
|
||||||
triggerList.triggerChangesZoneAll(game, sa);
|
triggerList.triggerChangesZoneAll(game, sa);
|
||||||
triggerList.clear();
|
|
||||||
|
|
||||||
game.fireEvent(new GameEventTokenCreated());
|
game.fireEvent(new GameEventTokenCreated());
|
||||||
|
|
||||||
|
|||||||
@@ -151,12 +151,9 @@ public class AttachEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
ZoneType previousZone = source.getZone().getZoneType();
|
ZoneType previousZone = source.getZone().getZoneType();
|
||||||
|
|
||||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard());
|
||||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
|
||||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
|
||||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
|
||||||
|
|
||||||
// The Spell_Permanent (Auras) version of this AF needs to
|
// The Spell_Permanent (Auras) version of this AF needs to
|
||||||
// move the card into play before Attaching
|
// move the card into play before Attaching
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ public class BalanceEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());
|
params.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield());
|
||||||
params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard());
|
params.put(AbilityKey.LastStateGraveyard, game.getLastStateBattlefield());
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateBattlefield());
|
||||||
|
|
||||||
for (int i = 0; i < players.size(); i++) {
|
for (int i = 0; i < players.size(); i++) {
|
||||||
Player p = players.get(i);
|
Player p = players.get(i);
|
||||||
int numToBalance = validCards.get(i).size() - min;
|
int numToBalance = validCards.get(i).size() - min;
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
CardLists.shuffle(cards);
|
CardLists.shuffle(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardZoneTable triggerList = new CardZoneTable();
|
final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||||
for (final Card c : cards) {
|
for (final Card c : cards) {
|
||||||
final Zone originZone = game.getZoneOf(c);
|
final Zone originZone = game.getZoneOf(c);
|
||||||
|
|
||||||
|
|||||||
@@ -468,7 +468,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
libraryPosition = pair.getValue();
|
libraryPosition = pair.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardZoneTable triggerList = new CardZoneTable();
|
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||||
|
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||||
|
|
||||||
|
final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
||||||
// changing zones for spells on the stack
|
// changing zones for spells on the stack
|
||||||
for (final SpellAbility tgtSA : getTargetSpells(sa)) {
|
for (final SpellAbility tgtSA : getTargetSpells(sa)) {
|
||||||
@@ -505,9 +508,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
chooser = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("Chooser"), sa).get(0);
|
chooser = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("Chooser"), sa).get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
|
||||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
|
||||||
|
|
||||||
// CR 401.4
|
// CR 401.4
|
||||||
if (destination.equals(ZoneType.Library) && !shuffle && tgtCards.size() > 1) {
|
if (destination.equals(ZoneType.Library) && !shuffle && tgtCards.size() > 1) {
|
||||||
if (sa.hasParam("RandomOrder")) {
|
if (sa.hasParam("RandomOrder")) {
|
||||||
@@ -1262,10 +1262,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final boolean imprint = sa.hasParam("Imprint");
|
final boolean imprint = sa.hasParam("Imprint");
|
||||||
|
|
||||||
boolean combatChanged = false;
|
boolean combatChanged = false;
|
||||||
final CardZoneTable triggerList = new CardZoneTable();
|
|
||||||
|
|
||||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||||
|
final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||||
|
|
||||||
for (Player player : HiddenOriginChoicesMap.keySet()) {
|
for (Player player : HiddenOriginChoicesMap.keySet()) {
|
||||||
boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary;
|
boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -246,7 +245,7 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort Chosen by SA order
|
// Sort Chosen by SA order
|
||||||
Collections.sort(chosen, new Comparator<AbilitySub>() {
|
chosen.sort(new Comparator<AbilitySub>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(AbilitySub o1, AbilitySub o2) {
|
public int compare(AbilitySub o1, AbilitySub o2) {
|
||||||
return Integer.compare(o1.getSVarInt("CharmOrder"), o2.getSVarInt("CharmOrder"));
|
return Integer.compare(o1.getSVarInt("CharmOrder"), o2.getSVarInt("CharmOrder"));
|
||||||
|
|||||||
@@ -89,10 +89,11 @@ public class DestroyAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard, sa);
|
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard, sa);
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
|
||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
|
||||||
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
|
|
||||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) {
|
if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.game.GameActionUtil;
|
|||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.card.CardZoneTable;
|
import forge.game.card.CardZoneTable;
|
||||||
@@ -61,7 +62,7 @@ public class DestroyEffect extends SpellAbilityEffect {
|
|||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
for (final Card tgtC : tgtCards) {
|
for (final Card tgtC : tgtCards) {
|
||||||
if (!tgtC.isInPlay()) {
|
if (!tgtC.isInPlay()) {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -30,12 +28,9 @@ public class ManifestEffect extends SpellAbilityEffect {
|
|||||||
// Most commonly "defined" is Top of Library
|
// Most commonly "defined" is Top of Library
|
||||||
final String defined = sa.getParamOrDefault("Defined", "TopOfLibrary");
|
final String defined = sa.getParamOrDefault("Defined", "TopOfLibrary");
|
||||||
|
|
||||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard());
|
||||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
|
||||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
|
||||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
|
||||||
|
|
||||||
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
||||||
CardCollection tgtCards;
|
CardCollection tgtCards;
|
||||||
@@ -73,8 +68,7 @@ public class ManifestEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("RememberManifested") && rem.isManifested()) {
|
if (sa.hasParam("RememberManifested") && rem.isManifested()) {
|
||||||
source.addRemembered(rem);
|
source.addRemembered(rem);
|
||||||
}
|
}
|
||||||
// 701.34d. If an effect instructs a player to manifest multiple cards from their library,
|
// CR 701.34d multiple cards are manifested one at a time
|
||||||
// those cards are manifested one at a time.
|
|
||||||
triggerList.put(origin, ZoneType.Battlefield, rem);
|
triggerList.put(origin, ZoneType.Battlefield, rem);
|
||||||
triggerList.triggerChangesZoneAll(game, sa);
|
triggerList.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard, sa);
|
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard, sa);
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
|
||||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||||
|
|
||||||
for (Card sac : list) {
|
for (Card sac : list) {
|
||||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
|||||||
final boolean destroy = sa.hasParam("Destroy");
|
final boolean destroy = sa.hasParam("Destroy");
|
||||||
final boolean remSacrificed = sa.hasParam("RememberSacrificed");
|
final boolean remSacrificed = sa.hasParam("RememberSacrificed");
|
||||||
final boolean optional = sa.hasParam("Optional");
|
final boolean optional = sa.hasParam("Optional");
|
||||||
CardZoneTable table = new CardZoneTable();
|
|
||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||||
|
|
||||||
if (valid.equals("Self") && game.getZoneOf(card) != null) {
|
if (valid.equals("Self") && game.getZoneOf(card) != null) {
|
||||||
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
||||||
|
|||||||
@@ -4055,7 +4055,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public final int getCurrentLoyalty() {
|
public final int getCurrentLoyalty() {
|
||||||
return getCounters(CounterEnumType.LOYALTY);
|
return getCounters(CounterEnumType.LOYALTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setBaseLoyalty(final int n) {
|
public final void setBaseLoyalty(final int n) {
|
||||||
currentState.setBaseLoyalty(Integer.toString(n));
|
currentState.setBaseLoyalty(Integer.toString(n));
|
||||||
}
|
}
|
||||||
@@ -4066,13 +4065,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public final void setBaseDefense(final int n) {
|
public final void setBaseDefense(final int n) {
|
||||||
currentState.setBaseDefense(Integer.toString(n));
|
currentState.setBaseDefense(Integer.toString(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getBasePower() {
|
public final int getBasePower() {
|
||||||
return currentState.getBasePower();
|
return currentState.getBasePower();
|
||||||
}
|
}
|
||||||
public final int getBaseToughness() {
|
public final int getBaseToughness() {
|
||||||
return currentState.getBaseToughness();
|
return currentState.getBaseToughness();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setBasePower(final int n) {
|
public final void setBasePower(final int n) {
|
||||||
currentState.setBasePower(n);
|
currentState.setBasePower(n);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ public class CardProperty {
|
|||||||
}
|
}
|
||||||
} else if (property.startsWith("ExiledWithSourceLKI")) {
|
} else if (property.startsWith("ExiledWithSourceLKI")) {
|
||||||
List<Card> exiled = card.getZone().getCardsAddedThisTurn(null);
|
List<Card> exiled = card.getZone().getCardsAddedThisTurn(null);
|
||||||
|
exiled.sort(CardPredicates.compareByTimestamp());
|
||||||
int idx = exiled.lastIndexOf(card);
|
int idx = exiled.lastIndexOf(card);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -22,11 +22,35 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
|
|||||||
private CardCollection createdTokens = new CardCollection();
|
private CardCollection createdTokens = new CardCollection();
|
||||||
private PlayerCollection firstTimeTokenCreators = new PlayerCollection();
|
private PlayerCollection firstTimeTokenCreators = new PlayerCollection();
|
||||||
|
|
||||||
public CardZoneTable(Table<ZoneType, ZoneType, CardCollection> cardZoneTable) {
|
private CardCollectionView lastStateBattlefield;
|
||||||
|
private CardCollectionView lastStateGraveyard;
|
||||||
|
|
||||||
|
public CardZoneTable(CardZoneTable cardZoneTable) {
|
||||||
this.putAll(cardZoneTable);
|
this.putAll(cardZoneTable);
|
||||||
|
lastStateBattlefield = cardZoneTable.getLastStateBattlefield();
|
||||||
|
lastStateGraveyard = cardZoneTable.getLastStateGraveyard();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardZoneTable() {
|
public CardZoneTable() {
|
||||||
|
this(CardCollection.EMPTY, CardCollection.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardZoneTable(CardCollectionView lastStateBattlefield, CardCollectionView lastStateGraveyard) {
|
||||||
|
this.lastStateBattlefield = lastStateBattlefield;
|
||||||
|
this.lastStateGraveyard = lastStateGraveyard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardCollectionView getLastStateBattlefield() {
|
||||||
|
return lastStateBattlefield;
|
||||||
|
}
|
||||||
|
public CardCollectionView getLastStateGraveyard() {
|
||||||
|
return lastStateGraveyard;
|
||||||
|
}
|
||||||
|
public void setLastStateBattlefield(CardCollectionView lastState) {
|
||||||
|
this.lastStateBattlefield = lastState;
|
||||||
|
}
|
||||||
|
public void setLastStateGraveyard(CardCollectionView lastState) {
|
||||||
|
this.lastStateGraveyard = lastState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,12 +112,20 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
|
|||||||
}
|
}
|
||||||
if (origin != null) {
|
if (origin != null) {
|
||||||
for (ZoneType z : origin) {
|
for (ZoneType z : origin) {
|
||||||
|
CardCollectionView lkiLookup = CardCollection.EMPTY;
|
||||||
|
if (z == ZoneType.Battlefield) {
|
||||||
|
lkiLookup = lastStateBattlefield;
|
||||||
|
}
|
||||||
if (containsRow(z)) {
|
if (containsRow(z)) {
|
||||||
if (destination != null) {
|
if (destination != null) {
|
||||||
allCards.addAll(row(z).get(destination));
|
for (Card c : row(z).get(destination)) {
|
||||||
|
allCards.add(lkiLookup.get(c));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (CardCollection c : row(z).values()) {
|
for (CardCollection cc : row(z).values()) {
|
||||||
allCards.addAll(c);
|
for (Card c : cc) {
|
||||||
|
allCards.add(lkiLookup.get(c));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,6 @@ public class CostExile extends CostPartWithList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String exileMultiZoneCostString(boolean forKW, int xMin) {
|
public String exileMultiZoneCostString(boolean forKW, int xMin) {
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Exile ");
|
sb.append("Exile ");
|
||||||
String amount = this.getAmount();
|
String amount = this.getAmount();
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ public abstract class CostPartWithList extends CostPart {
|
|||||||
|
|
||||||
// always returns true, made this to inline with return
|
// always returns true, made this to inline with return
|
||||||
protected boolean executePayment(Player payer, SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
|
protected boolean executePayment(Player payer, SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
|
||||||
|
table.setLastStateBattlefield(payer.getGame().getLastStateBattlefield());
|
||||||
|
table.setLastStateGraveyard(payer.getGame().getLastStateGraveyard());
|
||||||
|
|
||||||
handleBeforePayment(payer, ability, targetCards);
|
handleBeforePayment(payer, ability, targetCards);
|
||||||
if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes.
|
if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes.
|
||||||
for (Card c: targetCards) {
|
for (Card c: targetCards) {
|
||||||
@@ -174,9 +177,7 @@ public abstract class CostPartWithList extends CostPart {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy table because the original get cleaned after the cost is done
|
table.triggerChangesZoneAll(payer.getGame(), ability);
|
||||||
final CardZoneTable copyTable = new CardZoneTable(table);
|
|
||||||
copyTable.triggerChangesZoneAll(payer.getGame(), ability);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ public class CostPayment extends ManaConversionMatrix {
|
|||||||
|
|
||||||
public static boolean handleOfferings(final SpellAbility sa, boolean test, boolean costIsPaid) {
|
public static boolean handleOfferings(final SpellAbility sa, boolean test, boolean costIsPaid) {
|
||||||
final Game game = sa.getHostCard().getGame();
|
final Game game = sa.getHostCard().getGame();
|
||||||
final CardZoneTable table = new CardZoneTable();
|
final CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
if (sa.isOffering()) {
|
if (sa.isOffering()) {
|
||||||
if (sa.getSacrificedAsOffering() == null) {
|
if (sa.getSacrificedAsOffering() == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class Untap extends Phase {
|
|||||||
c.setStartedTheTurnUntapped(c.isUntapped());
|
c.setStartedTheTurnUntapped(c.isUntapped());
|
||||||
}
|
}
|
||||||
|
|
||||||
CardZoneTable triggerList = new CardZoneTable();
|
CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
CardCollection bounceList = CardLists.getKeyword(list, "During your next untap step, as you untap your permanents, return CARDNAME to its owner's hand.");
|
CardCollection bounceList = CardLists.getKeyword(list, "During your next untap step, as you untap your permanents, return CARDNAME to its owner's hand.");
|
||||||
for (final Card c : bounceList) {
|
for (final Card c : bounceList) {
|
||||||
Card moved = game.getAction().moveToHand(c, null);
|
Card moved = game.getAction().moveToHand(c, null);
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ public class PlayerProperty {
|
|||||||
}
|
}
|
||||||
} else if (property.equals("EnchantedController")) {
|
} else if (property.equals("EnchantedController")) {
|
||||||
Card enchanting = source.getEnchantingCard();
|
Card enchanting = source.getEnchantingCard();
|
||||||
if (enchanting != null && !player.equals(enchanting.getController())) {
|
if (enchanting == null || !player.equals(enchanting.getController())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (property.equals("Chosen")) {
|
} else if (property.equals("Chosen")) {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class StaticAbilityDisableTriggers {
|
|||||||
if (table == null) {
|
if (table == null) {
|
||||||
table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||||
}
|
}
|
||||||
CardZoneTable filtered = new CardZoneTable();
|
CardZoneTable filtered = new CardZoneTable(table.getLastStateBattlefield(), table.getLastStateGraveyard());
|
||||||
boolean possiblyDisabled = false;
|
boolean possiblyDisabled = false;
|
||||||
|
|
||||||
// purge all forbidden causes from table
|
// purge all forbidden causes from table
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.trigger;
|
package forge.game.trigger;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -102,16 +101,14 @@ public class TriggerChangesZone extends Trigger {
|
|||||||
|
|
||||||
if (hasParam("ValidCard")) {
|
if (hasParam("ValidCard")) {
|
||||||
Card moved = (Card) runParams.get(AbilityKey.Card);
|
Card moved = (Card) runParams.get(AbilityKey.Card);
|
||||||
boolean leavesLKIZone = "Battlefield".equals(getParam("Origin"));
|
|
||||||
leavesLKIZone |= "Exile".equals(getParam("Origin")) && (moved.getZone().is(ZoneType.Graveyard) ||
|
|
||||||
moved.getZone().is(ZoneType.Command) || hasParam("UseLKI"));
|
|
||||||
|
|
||||||
if (leavesLKIZone) {
|
// CR 603.10a leaves battlefield or GY look back in time
|
||||||
|
if ("Battlefield".equals(getParam("Origin"))
|
||||||
|
|| ("Graveyard".equals(getParam("Origin")) && !"Battlefield".equals(getParam("Destination")))) {
|
||||||
moved = (Card) runParams.get(AbilityKey.CardLKI);
|
moved = (Card) runParams.get(AbilityKey.CardLKI);
|
||||||
}
|
} else if ("Battlefield".equals(runParams.get(AbilityKey.Destination))) {
|
||||||
if ("Battlefield".equals(runParams.get(AbilityKey.Destination))) {
|
|
||||||
List<Card> etbLKI = moved.getController().getZone(ZoneType.Battlefield).getCardsAddedThisTurn(null);
|
List<Card> etbLKI = moved.getController().getZone(ZoneType.Battlefield).getCardsAddedThisTurn(null);
|
||||||
Collections.sort(etbLKI, CardPredicates.compareByTimestamp());
|
etbLKI.sort(CardPredicates.compareByTimestamp());
|
||||||
moved = etbLKI.get(etbLKI.lastIndexOf(moved));
|
moved = etbLKI.get(etbLKI.lastIndexOf(moved));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package forge.game.trigger;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -23,13 +25,21 @@ public class TriggerChangesZoneAll extends Trigger {
|
|||||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||||
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||||
|
|
||||||
|
// leaves the GY trigger look back in time
|
||||||
|
if (Iterables.contains(getActiveZone(), ZoneType.Battlefield) && "Graveyard".equals(getParam("Origin"))
|
||||||
|
&& !table.getLastStateBattlefield().contains(getHostCard())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
|
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("ValidAmount")) {
|
if (hasParam("ValidAmount")) {
|
||||||
int right = AbilityUtils.calculateAmount(hostCard, getParam("ValidAmount").substring(2), this);
|
int right = AbilityUtils.calculateAmount(hostCard, getParam("ValidAmount").substring(2), this);
|
||||||
if (!Expressions.compare(this.filterCards(table).size(), getParam("ValidAmount").substring(0, 2), right)) { return false; }
|
if (!Expressions.compare(this.filterCards(table).size(), getParam("ValidAmount").substring(0, 2), right)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !filterCards(table).isEmpty();
|
return !filterCards(table).isEmpty();
|
||||||
|
|||||||
@@ -739,10 +739,8 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
if (sa.getHostCard().getController().equals(p)) {
|
if (sa.getHostCard().getController().equals(p)) {
|
||||||
simultaneousStackEntryList.remove(sa);
|
simultaneousStackEntryList.remove(sa);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (activator.equals(p)) {
|
||||||
if (activator.equals(p)) {
|
simultaneousStackEntryList.remove(sa);
|
||||||
simultaneousStackEntryList.remove(sa);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:B
|
|||||||
Types:Creature Human Cleric
|
Types:Creature Human Cleric
|
||||||
PT:1/1
|
PT:1/1
|
||||||
K:Lifelink
|
K:Lifelink
|
||||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.
|
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Card.Self+YouOwn | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.
|
||||||
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self+wasCastFromYourGraveyardByYou | Execute$ TrigExile | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.
|
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self+wasCastFromYourGraveyardByYou | Execute$ TrigExile | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.
|
||||||
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ Self | SubAbility$ DBToken | RememberChanged$ True
|
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ Self | SubAbility$ DBToken | RememberChanged$ True
|
||||||
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_5_5_demon_flying | TokenOwner$ You | ConditionDefined$ Remembered | ConditionPresent$ Card.Self | ConditionCompare$ GE1
|
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_5_5_demon_flying | TokenOwner$ You | ConditionDefined$ Remembered | ConditionPresent$ Card.Self | ConditionCompare$ GE1
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ Name:Azorius Aethermage
|
|||||||
ManaCost:1 W U
|
ManaCost:1 W U
|
||||||
Types:Creature Human Wizard
|
Types:Creature Human Wizard
|
||||||
PT:1/1
|
PT:1/1
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever a permanent is returned to your hand, you may pay {1}. If you do, draw a card.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Card.YouOwn | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever a permanent is returned to your hand, you may pay {1}. If you do, draw a card.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Card.YouOwn+Other | Execute$ TrigDraw | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever a permanent is returned to your hand, you may pay {1}. If you do, draw a card.
|
|
||||||
SVar:TrigDraw:AB$Draw | Cost$ 1 | Defined$ You | NumCards$ 1
|
SVar:TrigDraw:AB$Draw | Cost$ 1 | Defined$ You | NumCards$ 1
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Whenever a permanent is returned to your hand, you may pay {1}. If you do, draw a card.
|
Oracle:Whenever a permanent is returned to your hand, you may pay {1}. If you do, draw a card.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefi
|
|||||||
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1
|
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1
|
||||||
A:AB$ ManaReflected | Cost$ T | Valid$ Defined.Imprinted | ColorOrType$ Color | ReflectProperty$ Is | SpellDescription$ Add one mana of any of the exiled card's colors.
|
A:AB$ ManaReflected | Cost$ T | Valid$ Defined.Imprinted | ColorOrType$ Color | ReflectProperty$ Is | SpellDescription$ Add one mana of any of the exiled card's colors.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget
|
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Execute$ DBForget
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
SVar:NeedsToPlayVar:Z GE1
|
SVar:NeedsToPlayVar:Z GE1
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefi
|
|||||||
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Exile
|
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Exile
|
||||||
S:Mode$ CantBeCast | ValidCard$ Card.sharesNameWith Imprinted | Description$ Players can't cast spells with the same name as the exiled card.
|
S:Mode$ CantBeCast | ValidCard$ Card.sharesNameWith Imprinted | Description$ Players can't cast spells with the same name as the exiled card.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
Oracle:Imprint — When Exclusion Ritual enters the battlefield, exile target nonland permanent.\nPlayers can't cast spells with the same name as the exiled card.
|
Oracle:Imprint — When Exclusion Ritual enters the battlefield, exile target nonland permanent.\nPlayers can't cast spells with the same name as the exiled card.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ T:Mode$ TapsForMana | ValidCard$ Land.sharesNameWith Imprinted | Execute$ TrigMa
|
|||||||
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredCardController
|
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredCardController
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:NeedsToPlay:Land.Basic+YouCtrl
|
SVar:NeedsToPlay:Land.Basic+YouCtrl
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.s
|
|||||||
SVar:TrigDamage:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 2
|
SVar:TrigDamage:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 2
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget
|
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Execute$ DBForget
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
Oracle:Imprint — When Invader Parasite enters the battlefield, exile target land.\nWhenever a land with the same name as the exiled card enters the battlefield under an opponent's control, Invader Parasite deals 2 damage to that player.
|
Oracle:Imprint — When Invader Parasite enters the battlefield, exile target land.\nWhenever a land with the same name as the exiled card enters the battlefield under an opponent's control, Invader Parasite deals 2 damage to that player.
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ K:Flying
|
|||||||
T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn.
|
T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn.
|
||||||
SVar:TrigEffect:DB$ Effect | StaticAbilities$ OppCantCast | RememberObjects$ TriggeredCard | Duration$ UntilYourNextTurn
|
SVar:TrigEffect:DB$ Effect | StaticAbilities$ OppCantCast | RememberObjects$ TriggeredCard | Duration$ UntilYourNextTurn
|
||||||
SVar:OppCantCast:Mode$ CantBeCast | ValidCard$ Card.cmcEQX | Caster$ Opponent | Description$ Your opponents can't cast spells with the same mana value as that spell until your next turn.
|
SVar:OppCantCast:Mode$ CantBeCast | ValidCard$ Card.cmcEQX | Caster$ Opponent | Description$ Your opponents can't cast spells with the same mana value as that spell until your next turn.
|
||||||
SVar:X:Remembered$CardManaCost
|
SVar:X:Remembered$CardManaCostLKI
|
||||||
Oracle:Flying\nWhenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn.
|
Oracle:Flying\nWhenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
|
|||||||
SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | Imprint$ True
|
SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | Imprint$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Creature | CheckSVar$ CheckCreature | SVarCompare$ GE1 | Description$ CARDNAME has protection from each of the exiled card's card types. (Artifact, creature, enchantment, instant, land, planeswalker, sorcery, and tribal are card types.)
|
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Creature | CheckSVar$ CheckCreature | SVarCompare$ GE1 | Description$ CARDNAME has protection from each of the exiled card's card types. (Artifact, creature, enchantment, instant, land, planeswalker, sorcery, and tribal are card types.)
|
||||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Land | CheckSVar$ CheckLand | SVarCompare$ GE1
|
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Land | CheckSVar$ CheckLand | SVarCompare$ GE1
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ SVar:TrigExile:DB$ ChangeZone | Imprint$ True | ValidTgts$ Creature.nonToken | T
|
|||||||
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigReset | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigReset | Static$ True
|
||||||
SVar:TrigReset:DB$ Cleanup | ClearImprinted$ True
|
SVar:TrigReset:DB$ Cleanup | ClearImprinted$ True
|
||||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ Y | Description$ CARDNAME gets +X/+Y, where X is the exiled creature card's power and Y is its toughness.
|
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ Y | Description$ CARDNAME gets +X/+Y, where X is the exiled creature card's power and Y is its toughness.
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:X:Imprinted$CardPower
|
SVar:X:Imprinted$CardPower
|
||||||
SVar:Y:Imprinted$CardToughness
|
SVar:Y:Imprinted$CardToughness
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exil
|
|||||||
S:Mode$ ReduceCost | ValidCard$ Card.sharesCardTypeWith Imprinted | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Spells you cast that share a card type with the exiled card cost {2} less to cast.
|
S:Mode$ ReduceCost | ValidCard$ Card.sharesCardTypeWith Imprinted | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Spells you cast that share a card type with the exiled card cost {2} less to cast.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget
|
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Execute$ DBForget
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:NeedsToPlayVar:Y GE1
|
SVar:NeedsToPlayVar:Y GE1
|
||||||
SVar:Y:Count$ValidHand Permanent.nonLand+YouOwn
|
SVar:Y:Count$ValidHand Permanent.nonLand+YouOwn
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ T:Mode$ DamageDone | ValidSource$ Creature.EquippedBy | ValidTarget$ Player | Co
|
|||||||
SVar:TrigSpell:DB$ Play | Defined$ Imprinted | Amount$ All | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | CopyCard$ True
|
SVar:TrigSpell:DB$ Play | Defined$ Imprinted | Amount$ All | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | CopyCard$ True
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
#Amount$ All | CopyOnce$ True for Strionic Resonator
|
#Amount$ All | CopyOnce$ True for Strionic Resonator
|
||||||
SVar:NeedsToPlayVar:Z GE1
|
SVar:NeedsToPlayVar:Z GE1
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ Types:Creature Human Soldier
|
|||||||
PT:4/3
|
PT:4/3
|
||||||
K:Flying
|
K:Flying
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters the battlefield, return two creatures you control to their owner's hand.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters the battlefield, return two creatures you control to their owner's hand.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another creature is returned to your hand from the battlefield, create a 1/1 white Soldier creature token.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Card.Self,Creature.YouOwn+Other | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another creature is returned to your hand from the battlefield, create a 1/1 white Soldier creature token.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Hand | ValidCard$ Creature.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another creature is returned to your hand from the battlefield, create a 1/1 white Soldier creature token.
|
|
||||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_soldier | TokenOwner$ You
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_soldier | TokenOwner$ You
|
||||||
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Hand | Hidden$ True | ChangeType$ Creature.YouCtrl | ChangeNum$ 2 | Mandatory$ True
|
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Hand | Hidden$ True | ChangeType$ Creature.YouCtrl | ChangeNum$ 2 | Mandatory$ True
|
||||||
SVar:NeedsToPlayVar:Z GE2
|
SVar:NeedsToPlayVar:Z GE2
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Library | Destination$ E
|
|||||||
S:Mode$ Continuous | Affected$ Card.EquippedBy | AddPower$ X | AddToughness$ X | Description$ Equipped creature gets +1/+1 for each land on the battlefield with the same name as the exiled card.
|
S:Mode$ Continuous | Affected$ Card.EquippedBy | AddPower$ X | AddToughness$ X | Description$ Equipped creature gets +1/+1 for each land on the battlefield with the same name as the exiled card.
|
||||||
SVar:X:Count$Valid Land.sharesNameWith Imprinted
|
SVar:X:Count$Valid Land.sharesNameWith Imprinted
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
|
||||||
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
|
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSourceLKI | Origin$ Exile | Execute$ DBForget | Static$ True
|
||||||
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:The Lost and the Damned
|
Name:The Lost and the Damned
|
||||||
ManaCost:1 U R
|
ManaCost:1 U R
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
T:Mode$ ChangesZone | Origin$ Graveyard,Exile,CommandZone,Library | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever a land enters the battlefield under your control from anywhere other than your hand or you cast a spell from anywhere other than your hand, create 3/3 red Spawn creature token.
|
T:Mode$ ChangesZone | Origin$ Graveyard,Exile,Command,Library | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever a land enters the battlefield under your control from anywhere other than your hand or you cast a spell from anywhere other than your hand, create 3/3 red Spawn creature token.
|
||||||
T:Mode$ SpellCast | ValidCard$ Card.wasNotCastFromYourHand | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ Whenever a land enters the battlefield under your control from anywhere other than your hand or you cast a spell from anywhere other than your hand, create 3/3 red Spawn creature token.
|
T:Mode$ SpellCast | ValidCard$ Card.wasNotCastFromYourHand | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ Whenever a land enters the battlefield under your control from anywhere other than your hand or you cast a spell from anywhere other than your hand, create 3/3 red Spawn creature token.
|
||||||
SVar:TrigToken:DB$ Token | TokenScript$ r_3_3_spawn
|
SVar:TrigToken:DB$ Token | TokenScript$ r_3_3_spawn
|
||||||
DeckHas:Ability$Token & Type$Spawn
|
DeckHas:Ability$Token & Type$Spawn
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ public class CardRanker {
|
|||||||
private static List<PaperCard> sortAndCreateList(List<Pair<Double, PaperCard>> cardScores) {
|
private static List<PaperCard> sortAndCreateList(List<Pair<Double, PaperCard>> cardScores) {
|
||||||
// even if some cards might be assigned the same rank we don't need randomization here
|
// even if some cards might be assigned the same rank we don't need randomization here
|
||||||
// as the limited variant is responsible for that during generation
|
// as the limited variant is responsible for that during generation
|
||||||
Collections.sort(cardScores, Collections.reverseOrder(new CardRankingComparator()));
|
cardScores.sort(Collections.reverseOrder(new CardRankingComparator()));
|
||||||
|
|
||||||
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
|
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
|
||||||
for (Pair<Double, PaperCard> pair : cardScores) {
|
for (Pair<Double, PaperCard> pair : cardScores) {
|
||||||
|
|||||||
Reference in New Issue
Block a user