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
|
||||
List<Pair<GameEntity, Integer>> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements();
|
||||
final GameEntity def = defender;
|
||||
Collections.sort(reqs, new Comparator<Pair<GameEntity, Integer>>() {
|
||||
reqs.sort(new Comparator<Pair<GameEntity, Integer>>() {
|
||||
@Override
|
||||
public int compare(Pair<GameEntity, Integer> r1, Pair<GameEntity, Integer> r2) {
|
||||
if (r1.getValue() == r2.getValue()) {
|
||||
|
||||
@@ -140,7 +140,7 @@ public class AiBlockController {
|
||||
ComputerUtilCard.sortByEvaluateCreature(attackers);
|
||||
CardLists.sortByPowerDesc(attackers);
|
||||
//move cards like Phage the Untouchable to the front
|
||||
Collections.sort(attackers, new Comparator<Card>() {
|
||||
attackers.sort(new Comparator<Card>() {
|
||||
@Override
|
||||
public int compare(final Card o1, final Card o2) {
|
||||
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) {
|
||||
int removed = 0;
|
||||
if (!prefs.isEmpty() && stillToRemove > 0) {
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(cType));
|
||||
prefs.sort(CardPredicates.compareByCounterType(cType));
|
||||
|
||||
for (Card prefCard : prefs) {
|
||||
// already enough removed
|
||||
@@ -667,7 +667,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
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) {
|
||||
int e = 0;
|
||||
|
||||
@@ -2451,7 +2451,7 @@ public class ComputerUtil {
|
||||
return goodChoices;
|
||||
}
|
||||
|
||||
Collections.sort(goodChoices, CardLists.TextLenComparator);
|
||||
goodChoices.sort(CardLists.TextLenComparator);
|
||||
|
||||
CardLists.sortByCmcDesc(goodChoices);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.ai;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -402,7 +401,7 @@ public class ComputerUtilAbility {
|
||||
return all;
|
||||
}
|
||||
// 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;
|
||||
for (int i = 0; i < all.size(); i++) {
|
||||
if (all.get(i).getApi() == ApiType.PermanentCreature) {
|
||||
|
||||
@@ -84,7 +84,7 @@ public class ComputerUtilCard {
|
||||
* @param 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.
|
||||
@@ -552,12 +552,13 @@ public class ComputerUtilCard {
|
||||
if (!Iterables.isEmpty(list)) {
|
||||
CardCollection cc = CardLists.filter(list,
|
||||
Predicates.or(CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
|
||||
Collections.sort(cc, CardLists.CmcComparatorInv);
|
||||
|
||||
if (cc.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
cc.sort(CardLists.CmcComparatorInv);
|
||||
|
||||
Card cheapest = cc.getLast();
|
||||
if (cheapest.hasSVar("DoNotDiscardIfAble")) {
|
||||
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
|
||||
public int compare(final Card card1, final Card card2) {
|
||||
return Integer.compare(manaCardMap.get(card1), manaCardMap.get(card2));
|
||||
@@ -149,7 +149,7 @@ public class ComputerUtilMana {
|
||||
System.out.println("Unsorted Abilities: " + newAbilities);
|
||||
}
|
||||
|
||||
Collections.sort(newAbilities, new Comparator<SpellAbility>() {
|
||||
newAbilities.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||
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> otherSortedAbilities = new ArrayList<>(newAbilities);
|
||||
|
||||
Collections.sort(prefSortedAbilities, new Comparator<SpellAbility>() {
|
||||
prefSortedAbilities.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
||||
@@ -206,7 +206,7 @@ public class ComputerUtilMana {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
Collections.sort(otherSortedAbilities, new Comparator<SpellAbility>() {
|
||||
otherSortedAbilities.sort(new Comparator<SpellAbility>() {
|
||||
@Override
|
||||
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
|
||||
if (ability1.getManaPart().mana(ability1).contains(preferredShard))
|
||||
@@ -1094,7 +1094,7 @@ public class ComputerUtilMana {
|
||||
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) {
|
||||
List<ManaCostShard> shardsToPay = Lists.newArrayList(cost.getDistinctShards());
|
||||
// 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
|
||||
public int compare(final ManaCostShard shard1, final ManaCostShard shard2) {
|
||||
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);
|
||||
}
|
||||
if (MyRandom.percentTrue(chance)) {
|
||||
Collections.sort(aiPlaneswalkers, CardPredicates.compareByCounterType(CounterEnumType.LOYALTY));
|
||||
aiPlaneswalkers.sort(CardPredicates.compareByCounterType(CounterEnumType.LOYALTY));
|
||||
for (Card pw : aiPlaneswalkers) {
|
||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
|
||||
|
||||
@@ -440,7 +440,7 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
|
||||
public String toCardList(String separator) {
|
||||
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();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
||||
@@ -794,7 +794,7 @@ public class Game {
|
||||
boolean planarControllerLost = false;
|
||||
boolean planarOwnerLost = false;
|
||||
boolean isMultiplayer = getPlayers().size() > 2;
|
||||
CardZoneTable triggerList = new CardZoneTable();
|
||||
CardZoneTable triggerList = new CardZoneTable(getLastStateBattlefield(), getLastStateGraveyard());
|
||||
|
||||
// 702.142f & 707.9
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
CardCollectionView lastBattlefield = null;
|
||||
CardCollectionView lastGraveyard = null;
|
||||
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();
|
||||
}
|
||||
CardCollectionView lastBattlefield = getLastState(AbilityKey.LastStateBattlefield, cause, params);
|
||||
CardCollectionView lastGraveyard = getLastState(AbilityKey.LastStateGraveyard, cause, params);
|
||||
|
||||
if (c.isSplitCard()) {
|
||||
boolean resetToOriginal = false;
|
||||
@@ -211,12 +195,10 @@ public class GameAction {
|
||||
// Make sure the card returns from the battlefield as the original card with two halves
|
||||
resetToOriginal = true;
|
||||
}
|
||||
} else {
|
||||
if (!zoneTo.is(ZoneType.Stack)) {
|
||||
} else if (!zoneTo.is(ZoneType.Stack)) {
|
||||
// For regular splits, recreate the original state unless the card is going to stack as one half
|
||||
resetToOriginal = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (resetToOriginal) {
|
||||
c.setState(CardStateName.Original, true);
|
||||
@@ -917,7 +899,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
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();
|
||||
for (Card card : cards) {
|
||||
if (cause != null) {
|
||||
@@ -964,7 +946,6 @@ public class GameAction {
|
||||
if (z.is(ZoneType.Battlefield)) {
|
||||
c.runLeavesPlayCommands();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
}
|
||||
|
||||
// Temporarily disable (if mode = true) actively checking static abilities.
|
||||
@@ -1264,7 +1244,7 @@ public class GameAction {
|
||||
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
|
||||
boolean checkAgain = false;
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||
|
||||
for (final Player p : game.getPlayers()) {
|
||||
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")) {
|
||||
int cmc = c.getCMC();
|
||||
|
||||
if (sq[0].contains("LKI") && ctb instanceof SpellAbility && !c.isInZone(ZoneType.Stack) && c.getManaCost() != null) {
|
||||
if (((SpellAbility) ctb).getXManaCostPaid() != null) {
|
||||
if (sq[0].contains("LKI") && !c.isInZone(ZoneType.Stack) && c.getManaCost() != null) {
|
||||
if (ctb instanceof SpellAbility && ((SpellAbility) ctb).getXManaCostPaid() != null) {
|
||||
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);
|
||||
|
||||
triggerList.triggerChangesZoneAll(game, sa);
|
||||
triggerList.clear();
|
||||
|
||||
game.fireEvent(new GameEventTokenCreated());
|
||||
|
||||
|
||||
@@ -151,12 +151,9 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
|
||||
ZoneType previousZone = source.getZone().getZoneType();
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard());
|
||||
|
||||
// The Spell_Permanent (Auras) version of this AF needs to
|
||||
// move the card into play before Attaching
|
||||
|
||||
@@ -49,9 +49,10 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());
|
||||
params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard());
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield());
|
||||
params.put(AbilityKey.LastStateGraveyard, game.getLastStateBattlefield());
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateBattlefield());
|
||||
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
Player p = players.get(i);
|
||||
int numToBalance = validCards.get(i).size() - min;
|
||||
|
||||
@@ -155,7 +155,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
CardLists.shuffle(cards);
|
||||
}
|
||||
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||
for (final Card c : cards) {
|
||||
final Zone originZone = game.getZoneOf(c);
|
||||
|
||||
|
||||
@@ -468,7 +468,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
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();
|
||||
// changing zones for spells on the stack
|
||||
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);
|
||||
}
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
// CR 401.4
|
||||
if (destination.equals(ZoneType.Library) && !shuffle && tgtCards.size() > 1) {
|
||||
if (sa.hasParam("RandomOrder")) {
|
||||
@@ -1262,10 +1262,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
final boolean imprint = sa.hasParam("Imprint");
|
||||
|
||||
boolean combatChanged = false;
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard);
|
||||
|
||||
for (Player player : HiddenOriginChoicesMap.keySet()) {
|
||||
boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -246,7 +245,7 @@ public class CharmEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// Sort Chosen by SA order
|
||||
Collections.sort(chosen, new Comparator<AbilitySub>() {
|
||||
chosen.sort(new Comparator<AbilitySub>() {
|
||||
@Override
|
||||
public int compare(AbilitySub o1, AbilitySub o2) {
|
||||
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);
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (Card c : list) {
|
||||
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.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
@@ -61,7 +62,7 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (final Card tgtC : tgtCards) {
|
||||
if (!tgtC.isInPlay()) {
|
||||
|
||||
@@ -2,8 +2,6 @@ package forge.game.ability.effects;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -30,12 +28,9 @@ public class ManifestEffect extends SpellAbilityEffect {
|
||||
// Most commonly "defined" is Top of Library
|
||||
final String defined = sa.getParamOrDefault("Defined", "TopOfLibrary");
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard());
|
||||
|
||||
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
||||
CardCollection tgtCards;
|
||||
@@ -73,8 +68,7 @@ public class ManifestEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("RememberManifested") && rem.isManifested()) {
|
||||
source.addRemembered(rem);
|
||||
}
|
||||
// 701.34d. If an effect instructs a player to manifest multiple cards from their library,
|
||||
// those cards are manifested one at a time.
|
||||
// CR 701.34d multiple cards are manifested one at a time
|
||||
triggerList.put(origin, ZoneType.Battlefield, rem);
|
||||
triggerList.triggerChangesZoneAll(game, sa);
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
||||
|
||||
list = GameActionUtil.orderCardsByTheirOwners(game, list, ZoneType.Graveyard, sa);
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||
|
||||
for (Card sac : list) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||
|
||||
@@ -100,9 +100,9 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
final boolean destroy = sa.hasParam("Destroy");
|
||||
final boolean remSacrificed = sa.hasParam("RememberSacrificed");
|
||||
final boolean optional = sa.hasParam("Optional");
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY);
|
||||
|
||||
if (valid.equals("Self") && game.getZoneOf(card) != null) {
|
||||
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
||||
|
||||
@@ -4055,7 +4055,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
public final int getCurrentLoyalty() {
|
||||
return getCounters(CounterEnumType.LOYALTY);
|
||||
}
|
||||
|
||||
public final void setBaseLoyalty(final int 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) {
|
||||
currentState.setBaseDefense(Integer.toString(n));
|
||||
}
|
||||
|
||||
public final int getBasePower() {
|
||||
return currentState.getBasePower();
|
||||
}
|
||||
public final int getBaseToughness() {
|
||||
return currentState.getBaseToughness();
|
||||
}
|
||||
|
||||
public final void setBasePower(final int n) {
|
||||
currentState.setBasePower(n);
|
||||
}
|
||||
|
||||
@@ -403,6 +403,7 @@ public class CardProperty {
|
||||
}
|
||||
} else if (property.startsWith("ExiledWithSourceLKI")) {
|
||||
List<Card> exiled = card.getZone().getCardsAddedThisTurn(null);
|
||||
exiled.sort(CardPredicates.compareByTimestamp());
|
||||
int idx = exiled.lastIndexOf(card);
|
||||
if (idx == -1) {
|
||||
return false;
|
||||
|
||||
@@ -22,11 +22,35 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
|
||||
private CardCollection createdTokens = new CardCollection();
|
||||
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);
|
||||
lastStateBattlefield = cardZoneTable.getLastStateBattlefield();
|
||||
lastStateGraveyard = cardZoneTable.getLastStateGraveyard();
|
||||
}
|
||||
|
||||
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) {
|
||||
for (ZoneType z : origin) {
|
||||
CardCollectionView lkiLookup = CardCollection.EMPTY;
|
||||
if (z == ZoneType.Battlefield) {
|
||||
lkiLookup = lastStateBattlefield;
|
||||
}
|
||||
if (containsRow(z)) {
|
||||
if (destination != null) {
|
||||
allCards.addAll(row(z).get(destination));
|
||||
for (Card c : row(z).get(destination)) {
|
||||
allCards.add(lkiLookup.get(c));
|
||||
}
|
||||
} else {
|
||||
for (CardCollection c : row(z).values()) {
|
||||
allCards.addAll(c);
|
||||
for (CardCollection cc : row(z).values()) {
|
||||
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) {
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Exile ");
|
||||
String amount = this.getAmount();
|
||||
|
||||
@@ -125,6 +125,9 @@ public abstract class CostPartWithList extends CostPart {
|
||||
|
||||
// always returns true, made this to inline with return
|
||||
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);
|
||||
if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes.
|
||||
for (Card c: targetCards) {
|
||||
@@ -174,9 +177,7 @@ public abstract class CostPartWithList extends CostPart {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy table because the original get cleaned after the cost is done
|
||||
final CardZoneTable copyTable = new CardZoneTable(table);
|
||||
copyTable.triggerChangesZoneAll(payer.getGame(), ability);
|
||||
table.triggerChangesZoneAll(payer.getGame(), ability);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
|
||||
public static boolean handleOfferings(final SpellAbility sa, boolean test, boolean costIsPaid) {
|
||||
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.getSacrificedAsOffering() == null) {
|
||||
return false;
|
||||
|
||||
@@ -128,7 +128,7 @@ public class Untap extends Phase {
|
||||
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.");
|
||||
for (final Card c : bounceList) {
|
||||
Card moved = game.getAction().moveToHand(c, null);
|
||||
|
||||
@@ -243,7 +243,7 @@ public class PlayerProperty {
|
||||
}
|
||||
} else if (property.equals("EnchantedController")) {
|
||||
Card enchanting = source.getEnchantingCard();
|
||||
if (enchanting != null && !player.equals(enchanting.getController())) {
|
||||
if (enchanting == null || !player.equals(enchanting.getController())) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("Chosen")) {
|
||||
|
||||
@@ -100,7 +100,7 @@ public class StaticAbilityDisableTriggers {
|
||||
if (table == null) {
|
||||
table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||
}
|
||||
CardZoneTable filtered = new CardZoneTable();
|
||||
CardZoneTable filtered = new CardZoneTable(table.getLastStateBattlefield(), table.getLastStateGraveyard());
|
||||
boolean possiblyDisabled = false;
|
||||
|
||||
// purge all forbidden causes from table
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package forge.game.trigger;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -102,16 +101,14 @@ public class TriggerChangesZone extends Trigger {
|
||||
|
||||
if (hasParam("ValidCard")) {
|
||||
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);
|
||||
}
|
||||
if ("Battlefield".equals(runParams.get(AbilityKey.Destination))) {
|
||||
} else if ("Battlefield".equals(runParams.get(AbilityKey.Destination))) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package forge.game.trigger;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
@@ -23,13 +25,21 @@ public class TriggerChangesZoneAll extends Trigger {
|
||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||
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))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasParam("ValidAmount")) {
|
||||
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();
|
||||
|
||||
@@ -739,13 +739,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
if (sa.getHostCard().getController().equals(p)) {
|
||||
simultaneousStackEntryList.remove(sa);
|
||||
}
|
||||
} else {
|
||||
if (activator.equals(p)) {
|
||||
} else if (activator.equals(p)) {
|
||||
simultaneousStackEntryList.remove(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final SpellAbilityStackInstance getInstanceMatchingSpellAbilityID(final SpellAbility sa) {
|
||||
for (final SpellAbilityStackInstance si : stack) {
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:B
|
||||
Types:Creature Human Cleric
|
||||
PT:1/1
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -2,8 +2,7 @@ Name:Azorius Aethermage
|
||||
ManaCost:1 W U
|
||||
Types:Creature Human Wizard
|
||||
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+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.
|
||||
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.
|
||||
SVar:TrigDraw:AB$Draw | Cost$ 1 | Defined$ You | NumCards$ 1
|
||||
AI:RemoveDeck:Random
|
||||
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
|
||||
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$ 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:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||
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
|
||||
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 | 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: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.
|
||||
|
||||
@@ -7,7 +7,7 @@ T:Mode$ TapsForMana | ValidCard$ Land.sharesNameWith Imprinted | Execute$ TrigMa
|
||||
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredCardController
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ 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:NeedsToPlay:Land.Basic+YouCtrl
|
||||
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
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ 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
|
||||
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.
|
||||
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: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.
|
||||
|
||||
@@ -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
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
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.
|
||||
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:X:Imprinted$CardPower
|
||||
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.
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ 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:NeedsToPlayVar:Y GE1
|
||||
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
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ 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
|
||||
#Amount$ All | CopyOnce$ True for Strionic Resonator
|
||||
SVar:NeedsToPlayVar:Z GE1
|
||||
|
||||
@@ -4,8 +4,7 @@ Types:Creature Human Soldier
|
||||
PT:4/3
|
||||
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$ 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$ 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.
|
||||
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.
|
||||
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: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.
|
||||
SVar:X:Count$Valid Land.sharesNameWith Imprinted
|
||||
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:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||
AI:RemoveDeck:Random
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:The Lost and the Damned
|
||||
ManaCost:1 U R
|
||||
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.
|
||||
SVar:TrigToken:DB$ Token | TokenScript$ r_3_3_spawn
|
||||
DeckHas:Ability$Token & Type$Spawn
|
||||
|
||||
@@ -192,7 +192,7 @@ public class CardRanker {
|
||||
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
|
||||
// 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());
|
||||
for (Pair<Double, PaperCard> pair : cardScores) {
|
||||
|
||||
Reference in New Issue
Block a user