mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Merge branch 'attachRework' into 'master'
Attach rework Closes #1447 See merge request core-developers/forge!5059
This commit is contained in:
@@ -860,7 +860,7 @@ public class AiController {
|
||||
return canPlayFromEffectAI((SpellPermanent)sa, false, true);
|
||||
}
|
||||
if (sa.usesTargeting()) {
|
||||
if (!sa.isTargetNumberValid() && !sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
||||
if (!sa.isTargetNumberValid() && !sa.getTargetRestrictions().hasCandidates(sa)) {
|
||||
return AiPlayDecision.TargetingFailed;
|
||||
}
|
||||
if (!StaticAbilityMustTarget.meetsMustTargetRestriction(sa)) {
|
||||
|
||||
@@ -1252,7 +1252,7 @@ public abstract class GameState {
|
||||
p.getGame().getAction().moveTo(ZoneType.Battlefield, c, null);
|
||||
} else {
|
||||
p.getZone(ZoneType.Hand).add(c);
|
||||
p.getGame().getAction().moveToPlay(c, null);
|
||||
p.getGame().getAction().moveToPlay(c, null, null);
|
||||
}
|
||||
|
||||
c.setTapped(tapped);
|
||||
@@ -1272,7 +1272,7 @@ public abstract class GameState {
|
||||
* <p>
|
||||
* processCardsForZone.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param data
|
||||
* an array of {@link java.lang.String} objects.
|
||||
* @param player
|
||||
@@ -1291,7 +1291,7 @@ public abstract class GameState {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Card c;
|
||||
boolean hasSetCurSet = false;
|
||||
if (cardinfo[0].startsWith("t:")) {
|
||||
@@ -1338,7 +1338,7 @@ public abstract class GameState {
|
||||
c.setState(CardStateName.Meld, true);
|
||||
} else if (info.startsWith("Modal")) {
|
||||
c.setState(CardStateName.Modal, true);
|
||||
}
|
||||
}
|
||||
else if (info.startsWith("OnAdventure")) {
|
||||
String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
||||
SpellAbility saAdventure = AbilityFactory.getAbility(abAdventure, c);
|
||||
|
||||
@@ -175,7 +175,7 @@ public abstract class SpellAbilityAi {
|
||||
// a mandatory SpellAbility with targeting but without candidates,
|
||||
// does not need to go any deeper
|
||||
if (sa.usesTargeting() && mandatory && !sa.isTargetNumberValid()
|
||||
&& !sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
||||
&& !sa.getTargetRestrictions().hasCandidates(sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public abstract class SpellAbilityAi {
|
||||
// sub-SpellAbility might use targets too
|
||||
if (sa.usesTargeting()) {
|
||||
// no Candidates, no adding to Stack
|
||||
if (!sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
||||
if (!sa.getTargetRestrictions().hasCandidates(sa)) {
|
||||
return false;
|
||||
}
|
||||
// but if it does, it should override this function
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
@@ -12,6 +8,7 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.ai.AiAttackController;
|
||||
import forge.ai.AiCardMemory;
|
||||
import forge.ai.AiController;
|
||||
import forge.ai.AiProps;
|
||||
@@ -52,6 +49,7 @@ import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
public class AttachAi extends SpellAbilityAi {
|
||||
@@ -1330,6 +1328,29 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is a SA that moves target attachment
|
||||
if ("MoveTgtAura".equals(sa.getParam("AILogic"))) {
|
||||
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
||||
list = CardLists.filter(list, Predicates.not(CardPredicates.isProtectedFrom(attachSource)));
|
||||
list = CardLists.filter(list, Predicates.or(CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card card) {
|
||||
return ComputerUtilCard.isUselessCreature(aiPlayer, card.getAttachedTo());
|
||||
}
|
||||
}));
|
||||
|
||||
return !list.isEmpty() ? ComputerUtilCard.getBestAI(list) : null;
|
||||
} else if ("Unenchanted".equals(sa.getParam("AILogic"))) {
|
||||
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
||||
CardCollection preferred = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card card) {
|
||||
return card.getAttachedCards().isEmpty();
|
||||
}
|
||||
});
|
||||
return preferred.isEmpty() ? Aggregates.random(list) : Aggregates.random(preferred);
|
||||
}
|
||||
|
||||
// is no attachment so no using attach
|
||||
if (!attachSource.isAttachment()) {
|
||||
return null;
|
||||
@@ -1442,10 +1463,11 @@ public class AttachAi extends SpellAbilityAi {
|
||||
* the logic
|
||||
* @return the card
|
||||
*/
|
||||
private static Card attachGeneralAI(final Player ai, final SpellAbility sa, final List<Card> list, final boolean mandatory,
|
||||
public static Card attachGeneralAI(final Player ai, final SpellAbility sa, final List<Card> list, final boolean mandatory,
|
||||
final Card attachSource, final String logic) {
|
||||
Player prefPlayer = ai.getWeakestOpponent();
|
||||
if ("Pump".equals(logic) || "Animate".equals(logic) || "Curiosity".equals(logic)) {
|
||||
Player prefPlayer = AiAttackController.choosePreferredDefenderPlayer(ai);
|
||||
if ("Pump".equals(logic) || "Animate".equals(logic) || "Curiosity".equals(logic) || "MoveTgtAura".equals(logic)
|
||||
|| "MoveAllAuras".equals(logic)) {
|
||||
prefPlayer = ai;
|
||||
}
|
||||
// Some ChangeType cards are beneficial, and PrefPlayer should be
|
||||
@@ -1470,14 +1492,13 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return chooseUnpreferred(mandatory, list);
|
||||
}
|
||||
|
||||
// Preferred list has at least one card in it to make to the actual
|
||||
// Logic
|
||||
// Preferred list has at least one card in it to make to the actual Logic
|
||||
Card c = null;
|
||||
if ("GainControl".equals(logic)) {
|
||||
c = attachAIControlPreference(sa, prefList, mandatory, attachSource);
|
||||
} else if ("Curse".equals(logic)) {
|
||||
c = attachAICursePreference(sa, prefList, mandatory, attachSource, ai);
|
||||
} else if ("Pump".equals(logic)) {
|
||||
} else if ("Pump".equals(logic) || (logic != null && logic.startsWith("Move"))) {
|
||||
c = attachAIPumpPreference(ai, sa, prefList, mandatory, attachSource);
|
||||
} else if ("Curiosity".equals(logic)) {
|
||||
c = attachAICuriosityPreference(sa, prefList, mandatory, attachSource);
|
||||
@@ -1725,7 +1746,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
return attachToCardAIPreferences(ai, sa, true);
|
||||
return attachGeneralAI(ai, sa, (List<Card>)options, !isOptional, sa.getHostCard(), sa.getParam("AILogic"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -59,6 +54,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.staticability.StaticAbilityMustTarget;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
public class ChangeZoneAi extends SpellAbilityAi {
|
||||
@@ -909,6 +905,19 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sa.hasParam("AttachAfter")) {
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (card.isValid(sa.getParam("AttachAfter"), ai, c, sa)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (list.size() < sa.getMinTargets()) {
|
||||
return false;
|
||||
@@ -1702,7 +1711,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Called when looking for creature to attach aura or equipment
|
||||
return ComputerUtilCard.getBestAI(options);
|
||||
return AttachAi.attachGeneralAI(ai, sa, (List<Card>)options, !isOptional, sa.getHostCard(), sa.getParam("AILogic"));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -1710,9 +1719,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
// Currently only used by Curse of Misfortunes, so this branch should never get hit
|
||||
// But just in case it does, just select the first option
|
||||
return Iterables.getFirst(options, null);
|
||||
// Called when attaching Aura to player
|
||||
return Aggregates.random(options);
|
||||
}
|
||||
|
||||
private boolean doSacAndReturnFromGraveLogic(final Player ai, final SpellAbility sa) {
|
||||
|
||||
@@ -203,15 +203,23 @@ public class Game {
|
||||
}
|
||||
}
|
||||
|
||||
public CardCollectionView copyLastStateBattlefield() {
|
||||
public CardCollectionView copyLastState(ZoneType type) {
|
||||
CardCollection result = new CardCollection();
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (final Player p : getPlayers()) {
|
||||
result.addAll(p.getZone(ZoneType.Battlefield).getLKICopy(cachedMap));
|
||||
result.addAll(p.getZone(type).getLKICopy(cachedMap));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public CardCollectionView copyLastStateBattlefield() {
|
||||
return copyLastState(ZoneType.Battlefield);
|
||||
}
|
||||
|
||||
public CardCollectionView copyLastStateGraveyard() {
|
||||
return copyLastState(ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
public void updateLastStateForCard(Card c) {
|
||||
if (c == null || c.getZone() == null) {
|
||||
return;
|
||||
|
||||
@@ -43,7 +43,6 @@ import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.effects.AttachEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
@@ -68,11 +67,13 @@ import forge.game.mulligan.MulliganService;
|
||||
import forge.game.player.GameLossReason;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityCantAttackBlock;
|
||||
import forge.game.staticability.StaticAbilityLayer;
|
||||
@@ -83,7 +84,9 @@ import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Expressions;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.ThreadUtil;
|
||||
import forge.util.Visitor;
|
||||
@@ -158,6 +161,28 @@ public class GameAction {
|
||||
return c;
|
||||
}
|
||||
|
||||
// Aura entering indirectly
|
||||
// need to check before it enters
|
||||
if (c.isAura() && !c.isAttachedToEntity() && toBattlefield && (zoneFrom == null || !zoneFrom.is(ZoneType.Stack))) {
|
||||
boolean found = false;
|
||||
if (Iterables.any(game.getPlayers(),PlayerPredicates.canBeAttached(c))) {
|
||||
found = true;
|
||||
}
|
||||
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(c))) {
|
||||
found = true;
|
||||
}
|
||||
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(c))) {
|
||||
found = true;
|
||||
}
|
||||
if (!found) {
|
||||
c.clearControllers();
|
||||
if (c.removeChangedState()) {
|
||||
c.updateStateForView();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// LKI is only needed when something is moved from the battlefield.
|
||||
// also it does messup with Blink Effects like Eldrazi Displacer
|
||||
if (fromBattlefield && zoneTo != null && !zoneTo.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Flashback)) {
|
||||
@@ -363,6 +388,27 @@ public class GameAction {
|
||||
|
||||
copied.getOwner().removeInboundToken(copied);
|
||||
|
||||
// Aura entering as Copy from stack
|
||||
// without targets it is sent to graveyard
|
||||
if (copied.isAura() && !copied.isAttachedToEntity() && toBattlefield) {
|
||||
if (zoneFrom != null && zoneFrom.is(ZoneType.Stack) && game.getStack().isResolving(c)) {
|
||||
boolean found = false;
|
||||
if (Iterables.any(game.getPlayers(),PlayerPredicates.canBeAttached(copied))) {
|
||||
found = true;
|
||||
}
|
||||
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(copied))) {
|
||||
found = true;
|
||||
}
|
||||
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(copied))) {
|
||||
found = true;
|
||||
}
|
||||
if (!found) {
|
||||
return moveToGraveyard(copied, cause, params);
|
||||
}
|
||||
}
|
||||
attachAuraOnIndirectEnterBattlefield(copied, params);
|
||||
}
|
||||
|
||||
// Handle merged permanent here so all replacement effects are already applied.
|
||||
CardCollection mergedCards = null;
|
||||
if (fromBattlefield && !toBattlefield && c.hasMergedCard()) {
|
||||
@@ -736,13 +782,6 @@ public class GameAction {
|
||||
c.setCastSA(null);
|
||||
}
|
||||
|
||||
if (c.isAura() && zoneTo.is(ZoneType.Battlefield) && ((zoneFrom == null) || !zoneFrom.is(ZoneType.Stack))
|
||||
&& !c.isEnchanting()) {
|
||||
// TODO Need a way to override this for Abilities that put Auras
|
||||
// into play attached to things
|
||||
AttachEffect.attachAuraOnIndirectEnterBattlefield(c);
|
||||
}
|
||||
|
||||
if (c.isRealCommander()) {
|
||||
c.setMoveToCommandZone(true);
|
||||
}
|
||||
@@ -842,13 +881,8 @@ public class GameAction {
|
||||
return moveTo(hand, c, cause, params);
|
||||
}
|
||||
|
||||
public final Card moveToPlay(final Card c, SpellAbility cause) {
|
||||
final PlayerZone play = c.getController().getZone(ZoneType.Battlefield);
|
||||
return moveTo(play, c, cause, null);
|
||||
}
|
||||
|
||||
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause) {
|
||||
return moveToPlay(c, p, cause, null);
|
||||
public final Card moveToPlay(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
return moveToPlay(c, c.getController(), cause, params);
|
||||
}
|
||||
|
||||
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
@@ -1225,6 +1259,7 @@ public class GameAction {
|
||||
}
|
||||
CardCollection noRegCreats = null;
|
||||
CardCollection desCreats = null;
|
||||
CardCollection unAttachList = new CardCollection();
|
||||
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.isCreature()) {
|
||||
// Rule 704.5f - Put into grave (no regeneration) for toughness <= 0
|
||||
@@ -1259,12 +1294,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
checkAgain |= stateBasedAction_Saga(c, table);
|
||||
checkAgain |= stateBasedAction704_attach(c, table); // Attachment
|
||||
|
||||
if (c.isCreature() && c.isAttachedToEntity()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached
|
||||
c.unattachFromEntity(c.getEntityAttachedTo());
|
||||
checkAgain = true;
|
||||
}
|
||||
checkAgain |= stateBasedAction704_attach(c, unAttachList); // Attachment
|
||||
|
||||
checkAgain |= stateBasedAction704_5r(c); // annihilate +1/+1 counters with -1/-1 ones
|
||||
|
||||
@@ -1291,10 +1321,30 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup aura
|
||||
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
||||
if (noRegCreats == null) {
|
||||
noRegCreats = new CardCollection();
|
||||
}
|
||||
noRegCreats.add(c);
|
||||
checkAgain = true;
|
||||
}
|
||||
if (checkAgain) {
|
||||
cardsToUpdateLKI.add(c);
|
||||
}
|
||||
}
|
||||
for (Card u : unAttachList) {
|
||||
u.unattachFromEntity(u.getEntityAttachedTo());
|
||||
|
||||
// cleanup aura
|
||||
if (u.isAura() && u.isInPlay() && !u.isEnchanting()) {
|
||||
if (noRegCreats == null) {
|
||||
noRegCreats = new CardCollection();
|
||||
}
|
||||
noRegCreats.add(u);
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
|
||||
// only check static abilities once after destroying all the creatures
|
||||
// (e.g. helpful for Erebos's Titan and another creature dealing lethal damage to each other simultaneously)
|
||||
@@ -1423,13 +1473,13 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stateBasedAction704_attach(Card c, CardZoneTable table) {
|
||||
private boolean stateBasedAction704_attach(Card c, CardCollection unAttachList) {
|
||||
boolean checkAgain = false;
|
||||
|
||||
if (c.isAttachedToEntity()) {
|
||||
final GameEntity ge = c.getEntityAttachedTo();
|
||||
if (!ge.canBeAttached(c, true)) {
|
||||
c.unattachFromEntity(ge);
|
||||
unAttachList.add(c);
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
@@ -1437,16 +1487,13 @@ public class GameAction {
|
||||
if (c.hasCardAttachments()) {
|
||||
for (final Card attach : Lists.newArrayList(c.getAttachedCards())) {
|
||||
if (!attach.isInPlay()) {
|
||||
attach.unattachFromEntity(c);
|
||||
unAttachList.add(attach);
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup aura
|
||||
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
||||
c.updateWasDestroyed(true);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
if (c.isCreature() && c.isAttachedToEntity()) { // Rule 704.5q - Creature attached to an object or player, becomes unattached
|
||||
unAttachList.add(c);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -2260,4 +2307,70 @@ public class GameAction {
|
||||
runParams.put(AbilityKey.Player, player);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DungeonCompleted, runParams, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach aura on indirect enter battlefield.
|
||||
*
|
||||
* @param source
|
||||
* the source
|
||||
* @return true, if successful
|
||||
*/
|
||||
public static boolean attachAuraOnIndirectEnterBattlefield(final Card source, Map<AbilityKey, Object> params) {
|
||||
// When an Aura ETB without being cast you can choose a valid card to
|
||||
// attach it to
|
||||
final SpellAbility aura = source.getFirstAttachSpell();
|
||||
|
||||
if (aura == null) {
|
||||
return false;
|
||||
}
|
||||
aura.setActivatingPlayer(source.getController());
|
||||
final Game game = source.getGame();
|
||||
final TargetRestrictions tgt = aura.getTargetRestrictions();
|
||||
|
||||
Player p = source.getController();
|
||||
if (tgt.canTgtPlayer()) {
|
||||
final FCollection<Player> players = new FCollection<>();
|
||||
|
||||
for (Player player : game.getPlayers()) {
|
||||
if (player.isValid(tgt.getValidTgts(), aura.getActivatingPlayer(), source, aura)) {
|
||||
players.add(player);
|
||||
}
|
||||
}
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (pa != null) {
|
||||
source.attachToEntity(pa);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
List<ZoneType> zones = Lists.newArrayList(tgt.getZone());
|
||||
CardCollection list = new CardCollection();
|
||||
|
||||
if (params != null) {
|
||||
if (zones.contains(ZoneType.Battlefield)) {
|
||||
list.addAll((CardCollectionView) params.get(AbilityKey.LastStateBattlefield));
|
||||
zones.remove(ZoneType.Battlefield);
|
||||
}
|
||||
if (zones.contains(ZoneType.Graveyard)) {
|
||||
list.addAll((CardCollectionView) params.get(AbilityKey.LastStateGraveyard));
|
||||
zones.remove(ZoneType.Graveyard);
|
||||
}
|
||||
}
|
||||
list.addAll(game.getCardsIn(zones));
|
||||
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), aura.getActivatingPlayer(), source, aura);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (o != null) {
|
||||
source.attachToEntity(game.getCardState(o), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ public enum AbilityKey {
|
||||
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
|
||||
IsMadness("IsMadness"),
|
||||
LastStateBattlefield("LastStateBattlefield"),
|
||||
LastStateGraveyard("LastStateGraveyard"),
|
||||
LifeAmount("LifeAmount"), //TODO confirm that this and LifeGained can be merged
|
||||
LifeGained("LifeGained"),
|
||||
Mana("Mana"),
|
||||
|
||||
@@ -405,8 +405,12 @@ public class AbilityUtils {
|
||||
}
|
||||
|
||||
if (incR.length > 1 && !cards.isEmpty()) {
|
||||
final String excR = "Card." + incR[1];
|
||||
cards = CardLists.getValidCards(cards, excR.split(","), hostCard.getController(), hostCard, sa);
|
||||
String[] valids = incR[1].split(",");
|
||||
// need to add valids onto all of them
|
||||
for (int i = 0; i < valids.length; i++) {
|
||||
valids[i] = "Card." + valids[i];
|
||||
}
|
||||
cards = CardLists.getValidCards(cards, valids, hostCard.getController(), hostCard, sa);
|
||||
}
|
||||
|
||||
return cards;
|
||||
|
||||
@@ -1,73 +1,49 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import forge.GameCommand;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class AttachEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
|
||||
if (host.isAura() && sa.isSpell()) {
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
host.setController(sa.getActivatingPlayer(), 0);
|
||||
|
||||
ZoneType previousZone = host.getZone().getZoneType();
|
||||
|
||||
// The Spell_Permanent (Auras) version of this AF needs to
|
||||
// move the card into play before Attaching
|
||||
final Card c = game.getAction().moveToPlay(host, sa);
|
||||
sa.setHostCard(c);
|
||||
|
||||
ZoneType newZone = c.getZone().getZoneType();
|
||||
if (newZone != previousZone) {
|
||||
table.put(previousZone, newZone, c);
|
||||
}
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
}
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
CardCollection attachments;
|
||||
final List<GameObject> targets = getDefinedOrTargeted(sa, "Defined");
|
||||
GameObject attachTo;
|
||||
|
||||
if (targets.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
attachTo = targets.get(0);
|
||||
String attachToName = null;
|
||||
if (attachTo instanceof Card) {
|
||||
attachToName = CardTranslation.getTranslatedName(((Card)attachTo).getName());
|
||||
}
|
||||
else {
|
||||
attachToName = attachTo.toString();
|
||||
}
|
||||
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
|
||||
if (sa.hasParam("Choices")) {
|
||||
Player chooser = p;
|
||||
if (sa.hasParam("Chooser")) {
|
||||
chooser = Iterables.getFirst(AbilityUtils.getDefinedPlayers(source, sa.getParam("Chooser"), sa), null);
|
||||
};
|
||||
|
||||
if (sa.hasParam("Object")) {
|
||||
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
|
||||
} else if (sa.hasParam("Choices")) {
|
||||
ZoneType choiceZone = ZoneType.Battlefield;
|
||||
if (sa.hasParam("ChoiceZone")) {
|
||||
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||
@@ -76,23 +52,100 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
|
||||
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"), p, source, sa);
|
||||
|
||||
Card c = p.getController().chooseSingleEntityForEffect(choices, sa, title, null);
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", Iterables.getFirst(getDefinedEntitiesOrTargeted(sa, "Defined"), null));
|
||||
|
||||
Card c = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, params);
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
attachments = new CardCollection(c);
|
||||
} else if (sa.hasParam("Object")) {
|
||||
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
|
||||
} else {
|
||||
attachments = new CardCollection(source);
|
||||
}
|
||||
|
||||
if (attachments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntity attachTo;
|
||||
|
||||
if (sa.hasParam("Object") && sa.hasParam("Choices")) {
|
||||
ZoneType choiceZone = ZoneType.Battlefield;
|
||||
if (sa.hasParam("ChoiceZone")) {
|
||||
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||
}
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";
|
||||
|
||||
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"), p, source, sa);
|
||||
// Object + Choices means Attach Aura/Equipment onto new another card it can attach
|
||||
// if multiple attachments, all of them need to be able to attach to new card
|
||||
for (final Card attachment : attachments) {
|
||||
if (sa.hasParam("Move")) {
|
||||
Card e = attachment.getAttachedTo();
|
||||
if (e != null)
|
||||
choices.remove(e);
|
||||
}
|
||||
choices = CardLists.filter(choices, CardPredicates.canBeAttached(attachment));
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attachments", attachments);
|
||||
|
||||
attachTo = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, params);
|
||||
} else {
|
||||
FCollection<GameEntity> targets = new FCollection<>(getDefinedEntitiesOrTargeted(sa, "Defined"));
|
||||
if (targets.isEmpty()) {
|
||||
return;
|
||||
} else {
|
||||
String title = Localizer.getInstance().getMessage("lblChoose");
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attachments", attachments);
|
||||
attachTo = chooser.getController().chooseSingleEntityForEffect(targets, sa, title, params);
|
||||
}
|
||||
}
|
||||
|
||||
String attachToName = null;
|
||||
if (attachTo == null) {
|
||||
return;
|
||||
} else if (attachTo instanceof Card) {
|
||||
attachToName = CardTranslation.getTranslatedName(((Card)attachTo).getName());
|
||||
} else {
|
||||
attachToName = attachTo.toString();
|
||||
}
|
||||
|
||||
// If Cast Targets will be checked on the Stack
|
||||
for (final Card attachment : attachments) {
|
||||
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
||||
if (sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message))
|
||||
// TODO add params for message
|
||||
continue;
|
||||
handleAttachment(attachment, attachTo, sa);
|
||||
|
||||
attachment.attachToEntity(attachTo);
|
||||
}
|
||||
|
||||
if (source.isAura() && sa.isSpell()) {
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
source.setController(sa.getActivatingPlayer(), 0);
|
||||
|
||||
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);
|
||||
|
||||
// The Spell_Permanent (Auras) version of this AF needs to
|
||||
// move the card into play before Attaching
|
||||
final Card c = game.getAction().moveToPlay(source, source.getController(), sa, moveParams);
|
||||
|
||||
ZoneType newZone = c.getZone().getZoneType();
|
||||
if (newZone != previousZone) {
|
||||
table.put(previousZone, newZone, c);
|
||||
}
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,121 +161,4 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
sb.append(Lang.joinHomogenous(targets));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle attachment.
|
||||
*
|
||||
* @param card
|
||||
* the card
|
||||
* @param o
|
||||
* the o
|
||||
*/
|
||||
public static void handleAttachment(final Card card, final Object o, final SpellAbility sa) {
|
||||
if (card == null) { return; }
|
||||
|
||||
if (o instanceof Card) {
|
||||
final Card c = (Card) o;
|
||||
if (card.isAura()) {
|
||||
// Most Auras can enchant permanents, a few can Enchant cards in
|
||||
// graveyards
|
||||
// Spellweaver Volute, Dance of the Dead, Animate Dead
|
||||
// Although honestly, I'm not sure if the three of those could
|
||||
// handle being scripted
|
||||
// 303.4h: If the card can't be enchanted, the aura doesn't move
|
||||
if (c.canBeAttached(card)) {
|
||||
handleAura(card, c);
|
||||
}
|
||||
} else {
|
||||
card.attachToEntity(c);
|
||||
}
|
||||
} else if (o instanceof Player) {
|
||||
// Currently, a few cards can enchant players
|
||||
// Psychic Possession, Paradox Haze, Wheel of Sun and Moon, New
|
||||
// Curse cards
|
||||
final Player p = (Player) o;
|
||||
if (card.isAura()) {
|
||||
handleAura(card, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle aura.
|
||||
*
|
||||
* @param card
|
||||
* the card
|
||||
* @param tgt
|
||||
* the tgt
|
||||
*/
|
||||
public static void handleAura(final Card card, final GameEntity tgt) {
|
||||
final GameCommand onLeavesPlay = new GameCommand() {
|
||||
private static final long serialVersionUID = -639204333673364477L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final GameEntity entity = card.getEntityAttachedTo();
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
card.unattachFromEntity(entity);
|
||||
}
|
||||
}; // Command
|
||||
|
||||
card.addLeavesPlayCommand(onLeavesPlay);
|
||||
card.attachToEntity(tgt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach aura on indirect enter battlefield.
|
||||
*
|
||||
* @param source
|
||||
* the source
|
||||
* @return true, if successful
|
||||
*/
|
||||
public static boolean attachAuraOnIndirectEnterBattlefield(final Card source) {
|
||||
// When an Aura ETB without being cast you can choose a valid card to
|
||||
// attach it to
|
||||
final SpellAbility aura = source.getFirstAttachSpell();
|
||||
|
||||
if (aura == null) {
|
||||
return false;
|
||||
}
|
||||
aura.setActivatingPlayer(source.getController());
|
||||
final Game game = source.getGame();
|
||||
final TargetRestrictions tgt = aura.getTargetRestrictions();
|
||||
|
||||
Player p = source.getController();
|
||||
if (tgt.canTgtPlayer()) {
|
||||
final FCollection<Player> players = new FCollection<>();
|
||||
|
||||
for (Player player : game.getPlayers()) {
|
||||
if (player.isValid(tgt.getValidTgts(), aura.getActivatingPlayer(), source, aura)) {
|
||||
players.add(player);
|
||||
}
|
||||
}
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (pa != null) {
|
||||
handleAura(source, pa);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CardCollectionView list = game.getCardsIn(tgt.getZone());
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), aura.getActivatingPlayer(), source, aura);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (o != null) {
|
||||
handleAura(source, o);
|
||||
//source.enchantEntity((Card) o);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardUtil;
|
||||
@@ -55,6 +56,8 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
CardCollection cards;
|
||||
List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
final Game game = sa.getActivatingPlayer().getGame();
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
if ((!sa.usesTargeting() && !sa.hasParam("Defined")) || sa.hasParam("UseAllOriginZones")) {
|
||||
cards = new CardCollection(game.getCardsIn(origin));
|
||||
@@ -164,6 +167,8 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
|
||||
if (destination == ZoneType.Battlefield) {
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
@@ -174,14 +179,6 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
||||
source.removeRemembered(c);
|
||||
}
|
||||
|
||||
// Auras without Candidates stay in their current location
|
||||
if (c.isAura()) {
|
||||
final SpellAbility saAura = c.getFirstAttachSpell();
|
||||
if (saAura != null && !saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("Tapped")) {
|
||||
c.setTapped(true);
|
||||
}
|
||||
@@ -205,49 +202,49 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (remember != null) {
|
||||
final Card newSource = game.getCardState(source);
|
||||
newSource.addRemembered(movedCard);
|
||||
if (!source.isRemembered(movedCard)) {
|
||||
source.addRemembered(movedCard);
|
||||
}
|
||||
if (c.getMeldedWith() != null) {
|
||||
Card meld = game.getCardState(c.getMeldedWith(), null);
|
||||
if (meld != null) {
|
||||
newSource.addRemembered(meld);
|
||||
if (!source.isRemembered(meld)) {
|
||||
source.addRemembered(meld);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c.hasMergedCard()) {
|
||||
for (final Card card : c.getMergedCards()) {
|
||||
if (card == c) continue;
|
||||
newSource.addRemembered(card);
|
||||
if (!source.isRemembered(card)) {
|
||||
source.addRemembered(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remLKI && movedCard != null) {
|
||||
final Card lki = CardUtil.getLKICopy(c);
|
||||
game.getCardState(source).addRemembered(lki);
|
||||
if (!source.isRemembered(lki)) {
|
||||
source.addRemembered(lki);
|
||||
}
|
||||
}
|
||||
if (forget != null) {
|
||||
game.getCardState(source).removeRemembered(c);
|
||||
}
|
||||
if (imprint != null) {
|
||||
game.getCardState(source).addImprintedCard(movedCard);
|
||||
}
|
||||
if (destination == ZoneType.Battlefield) {
|
||||
movedCard.setTimestamp(ts);
|
||||
}
|
||||
|
||||
if (!movedCard.getZone().equals(originZone)) {
|
||||
if (remember != null) {
|
||||
final Card newSource = game.getCardState(source);
|
||||
newSource.addRemembered(movedCard);
|
||||
if (!source.isRemembered(movedCard)) {
|
||||
source.addRemembered(movedCard);
|
||||
}
|
||||
if (c.getMeldedWith() != null) {
|
||||
Card meld = game.getCardState(c.getMeldedWith(), null);
|
||||
if (meld != null) {
|
||||
newSource.addRemembered(meld);
|
||||
if (!source.isRemembered(meld)) {
|
||||
source.addRemembered(meld);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c.hasMergedCard()) {
|
||||
for (final Card card : c.getMergedCards()) {
|
||||
if (card == c) continue;
|
||||
newSource.addRemembered(card);
|
||||
if (!source.isRemembered(card)) {
|
||||
source.addRemembered(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remLKI && movedCard != null) {
|
||||
final Card lki = CardUtil.getLKICopy(c);
|
||||
game.getCardState(source).addRemembered(lki);
|
||||
if (!source.isRemembered(lki)) {
|
||||
source.addRemembered(lki);
|
||||
}
|
||||
}
|
||||
if (forget != null) {
|
||||
game.getCardState(source).removeRemembered(c);
|
||||
}
|
||||
if (imprint != null) {
|
||||
game.getCardState(source).addImprintedCard(movedCard);
|
||||
}
|
||||
if (destination == ZoneType.Battlefield) {
|
||||
movedCard.setTimestamp(ts);
|
||||
}
|
||||
|
||||
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
|
||||
|
||||
if (c.getMeldedWith() != null) {
|
||||
|
||||
@@ -496,6 +496,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
chooser = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("Chooser"), sa).get(0);
|
||||
}
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
for (final Card tgtC : tgtCards) {
|
||||
final Card gameCard = game.getCardState(tgtC, null);
|
||||
// gameCard is LKI in that case, the card is not in game anymore
|
||||
@@ -582,13 +585,22 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AttachedTo")) {
|
||||
CardCollection list = AbilityUtils.getDefinedCards(hostCard, sa.getParam("AttachedTo"), sa);
|
||||
if (list.isEmpty()) {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachedTo"), gameCard.getController(), gameCard, sa);
|
||||
list = CardLists.getValidCards(lastStateBattlefield, sa.getParam("AttachedTo"), hostCard.getController(), hostCard, sa);
|
||||
}
|
||||
|
||||
// only valid choices are when they could be attached
|
||||
// TODO for multiple Auras entering attached this way, need to use LKI info
|
||||
if (!list.isEmpty()) {
|
||||
list = CardLists.filter(list, CardPredicates.canBeAttached(gameCard));
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", gameCard);
|
||||
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", gameCard.toString()), params);
|
||||
gameCard.attachToEntity(attachedTo);
|
||||
|
||||
// TODO can't attach later or moveToPlay would attach indirectly
|
||||
// bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection
|
||||
gameCard.attachToEntity(game.getCardState(attachedTo), true);
|
||||
} else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
continue;
|
||||
}
|
||||
@@ -608,6 +620,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
if (sa.isReplacementAbility()) {
|
||||
ReplacementEffect re = sa.getReplacementEffect();
|
||||
moveParams.put(AbilityKey.ReplacementEffect, re);
|
||||
@@ -627,20 +641,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
hostCard.removeRemembered(gameCard);
|
||||
}
|
||||
|
||||
// Auras without Candidates stay in their current location
|
||||
if (gameCard.isAura()) {
|
||||
final SpellAbility saAura = gameCard.getFirstAttachSpell();
|
||||
if (saAura != null) {
|
||||
saAura.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
gameCard.removeChangedState();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
|
||||
if (sa.hasParam("FaceDown")) {
|
||||
gameCard.turnFaceDown(true);
|
||||
@@ -648,7 +648,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
movedCard = game.getAction().moveTo(gameCard.getController().getZone(destination), gameCard, sa, moveParams);
|
||||
if (sa.hasParam("Unearth")) {
|
||||
// below stuff only if it changed zones
|
||||
if (!movedCard.getZone().equals(originZone)) {
|
||||
continue;
|
||||
}
|
||||
if (sa.hasParam("Unearth") && movedCard.isInPlay()) {
|
||||
movedCard.setUnearthed(true);
|
||||
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false,
|
||||
game.getNextTimestamp(), 0, true);
|
||||
@@ -670,6 +674,19 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
combatChanged = true;
|
||||
}
|
||||
movedCard.setTimestamp(ts);
|
||||
if (sa.hasParam("AttachAfter") && movedCard.isAttachment()) {
|
||||
CardCollection list = AbilityUtils.getDefinedCards(hostCard, sa.getParam("AttachAfter"), sa);
|
||||
if (list.isEmpty()) {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), hostCard.getController(), hostCard, sa);
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(gameCard.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", gameCard);
|
||||
Card attachedTo = chooser.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
movedCard.attachToEntity(attachedTo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// might set before card is moved only for nontoken
|
||||
Card host = null;
|
||||
@@ -1166,6 +1183,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
boolean combatChanged = false;
|
||||
final CardZoneTable triggerList = new CardZoneTable();
|
||||
|
||||
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
|
||||
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
|
||||
|
||||
for (Player player : HiddenOriginChoicesMap.keySet()) {
|
||||
boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary;
|
||||
boolean shuffleMandatory = HiddenOriginChoicesMap.get(player).shuffleMandatory;
|
||||
@@ -1182,6 +1202,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
final Zone originZone = game.getZoneOf(c);
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
moveParams.put(AbilityKey.FoundSearchingLibrary, searchedLibrary);
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
|
||||
if (destination.equals(ZoneType.Library)) {
|
||||
movedCard = game.getAction().moveToLibrary(c, libraryPos, sa, moveParams);
|
||||
}
|
||||
@@ -1225,26 +1247,25 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("AttachedTo")) {
|
||||
if (sa.hasParam("AttachedTo") && c.isAttachment()) {
|
||||
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachedTo"), sa);
|
||||
if (list.isEmpty()) {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachedTo"), c.getController(), c, sa);
|
||||
list = CardLists.getValidCards(lastStateBattlefield, sa.getParam("AttachedTo"), source.getController(), source, sa);
|
||||
}
|
||||
// only valid choices are when they could be attached
|
||||
// TODO for multiple Auras entering attached this way, need to use LKI info
|
||||
if (!list.isEmpty()) {
|
||||
list = CardLists.filter(list, CardPredicates.canBeAttached(c));
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = null;
|
||||
if (list.size() > 1) {
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", c);
|
||||
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
}
|
||||
else {
|
||||
attachedTo = list.get(0);
|
||||
}
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", c);
|
||||
Card attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
|
||||
if (c.isAttachment()) {
|
||||
c.attachToEntity(attachedTo);
|
||||
}
|
||||
// TODO can't attach later or moveToPlay would attach indirectly
|
||||
// bypass canBeAttached to skip Protection checks when trying to attach multiple auras that would grant protection
|
||||
c.attachToEntity(game.getCardState(attachedTo), true);
|
||||
}
|
||||
else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
continue;
|
||||
@@ -1277,6 +1298,20 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
movedCard = game.getAction().moveToPlay(c, c.getController(), sa, moveParams);
|
||||
|
||||
movedCard.setTimestamp(ts);
|
||||
|
||||
if (sa.hasParam("AttachAfter") && movedCard.isAttachment()) {
|
||||
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa);
|
||||
if (list.isEmpty()) {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa);
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", movedCard);
|
||||
Card attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
movedCard.attachToEntity(attachedTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (destination.equals(ZoneType.Exile)) {
|
||||
movedCard = game.getAction().exile(c, sa, moveParams);
|
||||
|
||||
@@ -148,11 +148,6 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
tgtCard.clearRemembered();
|
||||
}
|
||||
|
||||
// check if clone is now an Aura that needs to be attached
|
||||
if (tgtCard.isAura() && !tgtCard.isInZone(ZoneType.Battlefield)) {
|
||||
AttachEffect.attachAuraOnIndirectEnterBattlefield(tgtCard);
|
||||
}
|
||||
|
||||
if (sa.hasParam("Duration")) {
|
||||
final Card cloneCard = tgtCard;
|
||||
// if clone is temporary, target needs old values back after (keep Death-Mask Duplicant working)
|
||||
|
||||
@@ -2,6 +2,7 @@ package forge.game.ability.effects;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -14,12 +15,18 @@ import forge.game.spellability.SpellAbility;
|
||||
public class ETBReplacementEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Game game = sa.getActivatingPlayer().getGame();
|
||||
final Card card = (Card) sa.getReplacingObject(AbilityKey.Card);
|
||||
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
|
||||
params.put(AbilityKey.ReplacementEffect, sa.getReplacementEffect());
|
||||
params.put(AbilityKey.LastStateBattlefield, sa.getReplacingObject(AbilityKey.LastStateBattlefield));
|
||||
params.put(AbilityKey.LastStateGraveyard, sa.getReplacingObject(AbilityKey.LastStateGraveyard));
|
||||
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
SpellAbility cause = (SpellAbility) root.getReplacingObject(AbilityKey.Cause);
|
||||
sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), cause, params);
|
||||
|
||||
game.getAction().moveToPlay(card, card.getController(), cause, params);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
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;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -23,6 +29,13 @@ public class ManifestEffect extends SpellAbilityEffect {
|
||||
// Most commonly "defined" is Top of Library
|
||||
final String defined = sa.hasParam("Defined") ? sa.getParam("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);
|
||||
|
||||
for (final Player p : getTargetPlayers(sa, "DefinedPlayer")) {
|
||||
if (sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
||||
CardCollection tgtCards;
|
||||
@@ -52,8 +65,8 @@ public class ManifestEffect extends SpellAbilityEffect {
|
||||
CardLists.shuffle(tgtCards);
|
||||
}
|
||||
|
||||
for (Card c : tgtCards) {
|
||||
Card rem = c.manifest(p, sa);
|
||||
for(Card c : tgtCards) {
|
||||
Card rem = c.manifest(p, sa, moveParams);
|
||||
if (sa.hasParam("RememberManifested") && rem != null && rem.isManifested()) {
|
||||
source.addRemembered(rem);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -27,7 +32,14 @@ public class PermanentEffect extends SpellAbilityEffect {
|
||||
|
||||
host.setController(sa.getActivatingPlayer(), 0);
|
||||
|
||||
final Card c = game.getAction().moveToPlay(host, sa);
|
||||
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);
|
||||
|
||||
final Card c = game.getAction().moveToPlay(host, host.getController(), sa, moveParams);
|
||||
sa.setHostCard(c);
|
||||
|
||||
// some extra for Dashing
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
@@ -21,6 +22,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardFactory;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
@@ -104,6 +106,14 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
||||
pumpKeywords.addAll(Arrays.asList(sa.getParam("PumpKeywords").split(" & ")));
|
||||
}
|
||||
List<Card> allTokens = Lists.newArrayList();
|
||||
|
||||
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);
|
||||
|
||||
for (final Table.Cell<Player, Card, Integer> c : tokenTable.cellSet()) {
|
||||
Card prototype = c.getColumnKey();
|
||||
Player creator = c.getRowKey();
|
||||
@@ -153,7 +163,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// Should this be catching the Card that's returned?
|
||||
Card moved = game.getAction().moveToPlay(tok, sa);
|
||||
Card moved = game.getAction().moveToPlay(tok, sa, moveParams);
|
||||
if (moved == null || moved.getZone() == null) {
|
||||
// in case token can't enter the battlefield, it isn't created
|
||||
triggerList.put(ZoneType.None, ZoneType.None, moved);
|
||||
|
||||
@@ -652,7 +652,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Card manifest(Player p, SpellAbility sa) {
|
||||
public Card manifest(Player p, SpellAbility sa, Map<AbilityKey, Object> params) {
|
||||
// Turn Face Down (even if it's DFC).
|
||||
// Sometimes cards are manifested while already being face down
|
||||
if (!turnFaceDown(true) && !isFaceDown()) {
|
||||
@@ -667,7 +667,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
// Mark this card as "manifested"
|
||||
setManifested(true);
|
||||
|
||||
Card c = game.getAction().moveToPlay(this, p, sa);
|
||||
Card c = game.getAction().moveToPlay(this, p, sa, params);
|
||||
if (c.isInPlay()) {
|
||||
c.setManifested(true);
|
||||
c.turnFaceDown(true);
|
||||
@@ -3454,7 +3454,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
public final void attachToEntity(final GameEntity entity) {
|
||||
if (!entity.canBeAttached(this)) {
|
||||
attachToEntity(entity, false);
|
||||
}
|
||||
public final void attachToEntity(final GameEntity entity, boolean overwrite) {
|
||||
if (!overwrite && !entity.canBeAttached(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.cost.Cost;
|
||||
@@ -110,7 +111,16 @@ public class CardFactoryUtil {
|
||||
if (!hostCard.isFaceDown()) {
|
||||
hostCard.setOriginalStateAsFaceDown();
|
||||
}
|
||||
hostCard.getGame().getAction().moveToPlay(hostCard, this);
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
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);
|
||||
|
||||
hostCard.getGame().getAction().moveToPlay(hostCard, this, moveParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -913,9 +913,9 @@ public class CardProperty {
|
||||
}
|
||||
} else {
|
||||
final String restriction = property.split("sharesControllerWith ")[1];
|
||||
if (restriction.startsWith("Remembered") || restriction.startsWith("Imprinted")) {
|
||||
CardCollection list = AbilityUtils.getDefinedCards(source, restriction, spellAbility);
|
||||
return !CardLists.filter(list, CardPredicates.sharesControllerWith(card)).isEmpty();
|
||||
CardCollection list = AbilityUtils.getDefinedCards(source, restriction, spellAbility);
|
||||
if (!Iterables.any(list, CardPredicates.sharesControllerWith(card))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (property.startsWith("sharesOwnerWith")) {
|
||||
|
||||
@@ -121,6 +121,15 @@ public final class PlayerPredicates {
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Player> canBeAttached(final Card aura) {
|
||||
return new Predicate<Player>() {
|
||||
@Override
|
||||
public boolean apply(final Player p) {
|
||||
return p.canBeAttached(aura);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Comparator<Player> compareByZoneSize(final ZoneType zone) {
|
||||
return new Comparator<Player>() {
|
||||
@Override
|
||||
|
||||
@@ -96,8 +96,7 @@ public class ReplaceMoved extends ReplacementEffect {
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.Card, runParams.get(AbilityKey.Affected));
|
||||
sa.setReplacingObject(AbilityKey.CardLKI, runParams.get(AbilityKey.CardLKI));
|
||||
sa.setReplacingObject(AbilityKey.Cause, runParams.get(AbilityKey.Cause));
|
||||
sa.setReplacingObjectsFrom(runParams, AbilityKey.CardLKI, AbilityKey.Cause, AbilityKey.LastStateBattlefield, AbilityKey.LastStateGraveyard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -754,6 +754,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public void setReplacingObject(final AbilityKey type, final Object o) {
|
||||
replacingObjects.put(type, o);
|
||||
}
|
||||
public void setReplacingObjectsFrom(final Map<AbilityKey, Object> repParams, final AbilityKey... types) {
|
||||
int typesLength = types.length;
|
||||
for (int i = 0; i < typesLength; i += 1) {
|
||||
AbilityKey type = types[i];
|
||||
if (repParams.containsKey(type)) {
|
||||
setReplacingObject(type, repParams.get(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetOnceResolved() {
|
||||
//resetPaidHash(); // FIXME: if uncommented, breaks Dragon Presence, e.g. Orator of Ojutai + revealing a Dragon from hand.
|
||||
|
||||
@@ -472,21 +472,27 @@ public class TargetRestrictions {
|
||||
*
|
||||
* @param sa
|
||||
* the sa
|
||||
* @param isTargeted
|
||||
* Check Valid Candidates and Targeting
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean hasCandidates(final SpellAbility sa, final boolean isTargeted) {
|
||||
final Game game = sa.getHostCard().getGame();
|
||||
for (Player player : game.getPlayers()) {
|
||||
if (sa.canTarget(player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public final boolean hasCandidates(final SpellAbility sa) {
|
||||
final Card srcCard = sa.getHostCard(); // should there be OrginalHost at any moment?
|
||||
final Game game = srcCard.getGame();
|
||||
|
||||
this.applyTargetTextChanges(sa);
|
||||
|
||||
final Card srcCard = sa.getHostCard(); // should there be OrginalHost at any moment?
|
||||
for (Player player : game.getPlayers()) {
|
||||
if (!player.isValid(this.validTgts, sa.getActivatingPlayer(), srcCard, sa)) {
|
||||
continue;
|
||||
}
|
||||
if (!sa.canTarget(player)) {
|
||||
continue;
|
||||
}
|
||||
if (sa.getTargets().contains(player)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.tgtZone.contains(ZoneType.Stack)) {
|
||||
// Stack Zone targets are considered later
|
||||
return true;
|
||||
@@ -495,7 +501,7 @@ public class TargetRestrictions {
|
||||
if (!c.isValid(this.validTgts, sa.getActivatingPlayer(), srcCard, sa)) {
|
||||
continue;
|
||||
}
|
||||
if (isTargeted && !sa.canTarget(c)) {
|
||||
if (!sa.canTarget(c)) {
|
||||
continue;
|
||||
}
|
||||
if (sa.getTargets().contains(c)) {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
Name:Aura Graft
|
||||
ManaCost:1 U
|
||||
Types:Instant
|
||||
A:SP$ GainControl | Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target Aura attached to a permanent | SubAbility$ ChooseNewHost | SpellDescription$ Gain control of target Aura that's attached to a permanent. Attach it to another permanent it can enchant.
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Permanent.CanBeEnchantedByTargeted+NotEnchantedByTargeted | ChoiceZone$ Battlefield | SubAbility$ ReEnchant | RememberChosen$ True | AILogic$ AtLeast1
|
||||
SVar:ReEnchant:DB$ Attach | Object$ ParentTarget | Defined$ Remembered
|
||||
A:SP$ GainControl | Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target Aura attached to a permanent | SubAbility$ ReEnchant | StackDescription$ SpellDescription | SpellDescription$ Gain control of target Aura that's attached to a permanent. Attach it to another permanent it can enchant.
|
||||
SVar:ReEnchant:DB$ Attach | Object$ ParentTarget | Move$ True | Choices$ Permanent | StackDescription$ None
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/aura_graft.jpg
|
||||
Oracle:Gain control of target Aura that's attached to a permanent. Attach it to another permanent it can enchant.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
Name:Crown of the Ages
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
A:AB$ Pump | Cost$ 4 T | Amount$ 1 | ValidTgts$ Aura.AttachedTo Creature | TgtPrompt$ Select target Aura attached to a creature| SubAbility$ ChooseNewHost | StackDescription$ None | SpellDescription$ Attach target Aura attached to a creature to another creature.
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.NotEnchantedByTargeted | ChoiceZone$ Battlefield | SubAbility$ CrownAttach | RememberChosen$ True | AILogic$ AtLeast1
|
||||
SVar:CrownAttach:DB$ Attach | Object$ ParentTarget | Defined$ Remembered | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Attach | Cost$ 4 T | ValidTgts$ Aura.AttachedTo Creature | TgtPrompt$ Select target Aura attached to a creature | Object$ Targeted | Choices$ Creature | Move$ True | StackDescription$ SpellDescription | SpellDescription$ Attach target Aura attached to a creature to another creature.
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/crown_of_the_ages.jpg
|
||||
Oracle:{4}, {T}: Attach target Aura attached to a creature to another creature.
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
Name:Enchantment Alteration
|
||||
ManaCost:U
|
||||
Types:Instant
|
||||
A:SP$ Pump | Cost$ U | ValidTgts$ Aura.AttachedTo Creature,Aura.AttachedTo Land | TgtPrompt$ Select target Aura attached to a creature or land | SubAbility$ DBRem | StackDescription$ None | SpellDescription$ Attach target Aura attached to a creature or land to another permanent of that type.
|
||||
SVar:DBRem:DB$ PumpAll | ValidCards$ Land.EnchantedBy Targeted,Creature.EnchantedBy Targeted | RememberAllPumped$ True | SubAbility$ ChooseNewHost
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Permanent.CanBeEnchantedByTargeted+NotEnchantedByTargeted+sharesCardTypeWith Remembered | ChoiceZone$ Battlefield | SubAbility$ AlterationAttach | AILogic$ AtLeast1
|
||||
SVar:AlterationAttach:DB$ Attach | Object$ ParentTarget | Defined$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/enchantment_alteration.jpg
|
||||
A:SP$ Attach | Cost$ U | ValidTgts$ Aura.AttachedTo Creature,Aura.AttachedTo Land | TgtPrompt$ Select target Aura attached to a creature or land | Object$ Targeted | Choices$ Permanent.sharesCardTypeWith AttachedBy Targeted | Move$ True | AILogic$ MoveTgtAura | StackDescription$ SpellDescription | SpellDescription$ Attach target Aura attached to a creature or land to another permanent of that type.
|
||||
Oracle:Attach target Aura attached to a creature or land to another permanent of that type.
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
Name:Fumble
|
||||
ManaCost:1 U
|
||||
Types:Instant
|
||||
A:SP$ Pump | Cost$ 1 U | IsCurse$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBRem | StackDescription$ SpellDescription | SpellDescription$ Return target creature to its owner's hand. Gain control of all Auras and Equipment that were attached to it, then attach them to another creature.
|
||||
SVar:DBRem:DB$ PumpAll | ValidCards$ Aura.AttachedTo Targeted,Equipment.AttachedTo Targeted | RememberAllPumped$ True | SubAbility$ DBBounce
|
||||
SVar:DBBounce:DB$ ChangeZone | Defined$ Targeted | Origin$ Battlefield | Destination$ Hand | SubAbility$ ChooseNewHost
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.CanBeEnchantedByAllRemembered | ChoiceZone$ Battlefield | SubAbility$ GainControl
|
||||
SVar:GainControl:DB$ GainControl | AllValid$ Equipment.IsRemembered,Enchantment.IsRemembered | NewController$ You | ConditionDefined$ ChosenCard | ConditionPresent$ Card | ConditionCompare$ EQ1 | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Object$ Remembered | Defined$ ChosenCard | SubAbility$ CleanUpEnchantments
|
||||
SVar:CleanUpEnchantments:DB$ ChangeZone | Origin$ All | Destination$ Graveyard | ChangeType$ Enchantment.IsRemembered | ConditionDefined$ ChosenCard | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ ControlEquipment
|
||||
SVar:ControlEquipment:DB$ GainControl | AllValid$ Equipment.IsRemembered | NewController$ You | ConditionDefined$ ChosenCard | ConditionPresent$ Card | ConditionCompare$ EQ0 | SubAbility$ DBCleanUp
|
||||
SVar:DBCleanUp:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:Return target creature to its owner's hand. Gain control of all Auras and Equipment that were attached to it, then attach them to another creature.
|
||||
A:SP$ ChangeZone | Cost$ 1 U | IsCurse$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | SubAbility$ GainControl | StackDescription$ SpellDescription | SpellDescription$ Return target creature to its owner's hand. Gain control of all Auras and Equipment that were attached to it, then attach them to another creature.
|
||||
SVar:GainControl:DB$ GainControl | Defined$ AttachedTo Targeted.Aura,Enchantment | NewController$ You | SubAbility$ DBAttach | StackDescription$ None
|
||||
SVar:DBAttach:DB$ Attach | Object$ AttachedTo Targeted.Aura,Enchantment | Choices$ Creature | StackDescription$ None
|
||||
Oracle:Return target creature to its owner's hand. Gain control of all Auras and Equipment that were attached to it, then attach them to another creature.
|
||||
|
||||
@@ -5,9 +5,7 @@ K:Enchant creature
|
||||
A:SP$ Attach | Cost$ 4 B | ValidTgts$ Creature | AILogic$ Pump
|
||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Deathtouch & Indestructible | Description$ Enchanted creature has deathtouch and indestructible.
|
||||
K:Morph:Sac<1/Creature.Other/another creature>
|
||||
R:Event$ TurnFaceUp | ValidCard$ Card.Self | ReplaceWith$ DBChoose | ActiveZones$ Battlefield | Description$ As CARDNAME is turned face up, you may attach it to a creature.
|
||||
SVar:DBChoose:DB$ ChooseCard | Choices$ Creature | ChoiceTitle$ Choose a creature | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Defined$ ChosenCard | Object$ Self | Optional$ True | AILogic$ Pump | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||
R:Event$ TurnFaceUp | ValidCard$ Card.Self | ReplaceWith$ DBAttach | ActiveZones$ Battlefield | Description$ As CARDNAME is turned face up, you may attach it to a creature.
|
||||
SVar:DBAttach:DB$ Attach | Choices$ Creature | ChoiceTitle$ Choose a creature | Object$ Self | Optional$ True | AILogic$ Pump
|
||||
AI:RemoveDeck:All
|
||||
Oracle:Enchant creature\nEnchanted creature has deathtouch and indestructible.\nMorph—Sacrifice another creature. (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)\nAs Gift of Doom is turned face up, you may attach it to a creature.
|
||||
|
||||
@@ -4,12 +4,6 @@ Types:Creature Faerie Wizard
|
||||
PT:2/4
|
||||
K:Flash
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRem | TriggerDescription$ When CARDNAME enters the battlefield, attach all Auras enchanting target permanent to another permanent with the same controller.
|
||||
SVar:TrigRem:DB$ Pump | ValidTgts$ Permanent | TgtPrompt$ Select target permanent to remove auras | ImprintCards$ Targeted | RememberObjects$ Valid Aura.AttachedTo Targeted | SubAbility$ DBNewHost
|
||||
SVar:DBNewHost:DB$ ChooseCard | Choices$ Permanent.IsNotImprinted+sharesControllerWith Imprinted+CanBeEnchantedByAllRemembered | SubAbility$ ClearImprint
|
||||
SVar:ClearImprint:DB$ Cleanup | ClearImprinted$ True | SubAbility$ DBMove
|
||||
SVar:DBMove:DB$ RepeatEach | RepeatCards$ Aura.IsRemembered | RepeatSubAbility$ DBAuraAttach | UseImprinted$ True | SubAbility$ DBCleanup
|
||||
SVar:DBAuraAttach:DB$ Attach | Object$ Imprinted | Defined$ ChosenCard
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/glamer_spinners.jpg
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAuraAttach | TriggerDescription$ When CARDNAME enters the battlefield, attach all Auras enchanting target permanent to another permanent with the same controller.
|
||||
SVar:TrigAuraAttach:DB$ Attach | ValidTgts$ Permanent | TgtPrompt$ Select target permanent to remove auras | Object$ Valid Aura.AttachedTo Targeted | Choices$ Permanent.sharesControllerWith Targeted | Move$ True | AILogic$ Unenchanted
|
||||
Oracle:Flash\nFlying\nWhen Glamer Spinners enters the battlefield, attach all Auras enchanting target permanent to another permanent with the same controller.
|
||||
|
||||
@@ -3,9 +3,7 @@ ManaCost:3
|
||||
Types:Artifact Equipment
|
||||
K:Equip:1
|
||||
K:Flash
|
||||
K:ETBReplacement:Other:ChooseC
|
||||
SVar:ChooseC:DB$ ChooseCard | Defined$ You | Choices$ Creature.YouCtrl+CanBeAttachedBy | Amount$ 1 | Mandatory$ True | SubAbility$ DBAttach | SpellDescription$ As CARDNAME enters the battlefield, choose a creature you control it could be attached to. If you do, it enters the battlefield attached to that creature.
|
||||
SVar:DBAttach:DB$ Attach | Object$ Self | Defined$ ChosenCard
|
||||
K:ETBReplacement:Other:DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Object$ Self | Choices$ Creature.YouCtrl | SpellDescription$ As CARDNAME enters the battlefield, choose a creature you control it could be attached to. If you do, it enters the battlefield attached to that creature.
|
||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creature gets +1/+1.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/grifters_blade.jpg
|
||||
Oracle:Flash\nAs Grifter's Blade enters the battlefield, choose a creature you control it could be attached to. If you do, it enters the battlefield attached to that creature.\nEquipped creature gets +1/+1.\nEquip {1}
|
||||
|
||||
@@ -7,7 +7,6 @@ SVar:TrigFlip:DB$ SetState | Defined$ Self | Mode$ Flip
|
||||
AI:RemoveDeck:Random
|
||||
DeckNeeds:Type$Aura
|
||||
SVar:EnchantMe:Multiple
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/kitsune_mystic.jpg
|
||||
AlternateMode:Flip
|
||||
Oracle:At the beginning of the end step, if Kitsune Mystic is enchanted by two or more Auras, flip it.
|
||||
|
||||
@@ -17,10 +16,6 @@ Name:Autumn-Tail, Kitsune Sage
|
||||
ManaCost:3 W
|
||||
Types:Legendary Creature Fox Wizard
|
||||
PT:4/5
|
||||
A:AB$ Pump | Cost$ 1 | Amount$ 1 | ValidTgts$ Aura.AttachedTo Creature | TgtPrompt$ Select target Aura attached to a creature | SubAbility$ ChooseNewHost | StackDescription$ None | SpellDescription$ Attach target Aura attached to a creature to another creature.
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.NotEnchantedByTargeted | ChoiceZone$ Battlefield | SubAbility$ KitsuneAttach | RememberChosen$ True | AILogic$ AtLeast1
|
||||
SVar:KitsuneAttach:DB$ Attach | Object$ ParentTarget | Defined$ Remembered | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Attach | Cost$ 1 | ValidTgts$ Aura.AttachedTo Creature | TgtPrompt$ Select target Aura attached to a creature | Object$ Targeted | Choices$ Creature | Move$ True | StackDescription$ SpellDescription | SpellDescription$ Attach target Aura attached to a creature to another creature.
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/autumn_tail_kitsune_sage.jpg
|
||||
Oracle:{1}: Attach target Aura attached to a creature to another creature.
|
||||
|
||||
@@ -4,9 +4,7 @@ Types:Enchantment Aura
|
||||
K:Enchant land
|
||||
A:SP$ Attach | Cost$ 1 G G | ValidTgts$ Land | AILogic$ Curse
|
||||
T:Mode$ Taps | ValidCard$ Card.AttachedBy | Execute$ TrigDestroy | TriggerDescription$ When enchanted land becomes tapped, destroy it. That land's controller may attach CARDNAME to a land of their choice.
|
||||
SVar:TrigDestroy:DB$ Destroy | Defined$ TriggeredCardLKICopy | SubAbility$ DBChoose
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ TriggeredCardController | Choices$ Land | AILogic$ OppPreferred | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Defined$ ChosenCard
|
||||
SVar:TrigDestroy:DB$ Destroy | Defined$ TriggeredCardLKICopy | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Object$ Self | Chooser$ TriggeredCardController | Choices$ Land | AILogic$ Curse
|
||||
SVar:NonStackingAttachEffect:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/kudzu.jpg
|
||||
Oracle:Enchant land\nWhen enchanted land becomes tapped, destroy it. That land's controller may attach Kudzu to a land of their choice.
|
||||
|
||||
@@ -3,10 +3,9 @@ ManaCost:3 W W
|
||||
Types:Legendary Planeswalker Nahiri
|
||||
Loyalty:3
|
||||
Text:CARDNAME can be your commander.
|
||||
A:AB$ Token | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ w_1_1_kor_soldier | TokenOwner$ You | LegacyImage$ w 1 1 kor soldier c14 | RememberTokens$ True | SubAbility$ DBChooseToken | SpellDescription$ Create a 1/1 white Kor Soldier creature token. You may attach an Equipment you control to it.
|
||||
SVar:DBChooseToken:DB$ ChooseCard | DefinedCards$ Remembered | Mandatory$ True | ChoiceTitle$ Choose a token | SubAbility$ DBAttach | StackDescription$ None
|
||||
SVar:DBAttach:DB$ Attach | Optional$ True | Choices$ Equipment.YouCtrl | ChoiceTitle$ Choose an Equipment you control | Defined$ ChosenCard | SubAbility$ DBCleanup | StackDescription$ None
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
|
||||
A:AB$ Token | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ w_1_1_kor_soldier | TokenOwner$ You | LegacyImage$ w 1 1 kor soldier c14 | RememberTokens$ True | SubAbility$ DBAttach | SpellDescription$ Create a 1/1 white Kor Soldier creature token. You may attach an Equipment you control to it.
|
||||
SVar:DBAttach:DB$ Attach | Optional$ True | Choices$ Equipment.YouCtrl | ChoiceTitle$ Choose an Equipment you control | Defined$ Remembered | SubAbility$ DBCleanup | StackDescription$ None
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ ChangeZone | Cost$ SubCounter<2/LOYALTY> | Origin$ Hand,Graveyard | Destination$ Battlefield | Hidden$ True | Planeswalker$ True | ChangeType$ Equipment.YouCtrl | Optional$ True | SpellDescription$ You may put an Equipment card from your hand or graveyard onto the battlefield.
|
||||
A:AB$ Token | Cost$ SubCounter<10/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenAmount$ 1 | TokenScript$ stoneforged_blade | LegacyImage$ stoneforged blade c14 | TokenOwner$ You | SpellDescription$ Create a colorless Equipment artifact token named Stoneforged Blade. It has indestructible, "Equipped creature gets +5/+5 and has double strike," and equip {0}.
|
||||
DeckHas:Ability$Token
|
||||
|
||||
@@ -4,10 +4,8 @@ Types:Enchantment Aura
|
||||
K:Enchant creature or land
|
||||
A:SP$ Attach | Cost$ 4 B B | ValidTgts$ Creature,Land | AILogic$ Curse
|
||||
S:Mode$ Continuous | Affected$ Card.AttachedBy | AddTrigger$ NettlevineTrig | Description$ Enchanted permanent has "At the beginning of your end step, sacrifice this permanent and attach CARDNAME to a creature or land you control."
|
||||
SVar:NettlevineTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ NettlevineSac | TriggerDescription$ At the beginning of your end step, sacrifice CARDNAME and attach ORIGINALHOST to a creature or land you control.
|
||||
SVar:NettlevineSac:DB$ Sacrifice | Defined$ Self | SubAbility$ NettlevineChoose
|
||||
SVar:NettlevineChoose:DB$ ChooseCard | Defined$ You | Choices$ Creature.YouCtrl,Land.YouCtrl | AILogic$ WorstCard | Mandatory$ True | SubAbility$ NettlevineAttach
|
||||
SVar:NettlevineAttach:DB$ Attach | Defined$ ChosenCard | Object$ OriginalHost | SubAbility$ NettlevineCleanup
|
||||
SVar:NettlevineCleanup:DB$ Pump | ClearChosenCard$ True
|
||||
SVar:NettlevineTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ NettlevineSac | TriggerDescription$ At the beginning of your end step, sacrifice CARDNAME and ORIGINALHOST to a creature or land you control.
|
||||
SVar:NettlevineSac:DB$ Sacrifice | Defined$ Self | SubAbility$ NettlevineAttach
|
||||
SVar:NettlevineAttach:DB$ Attach | Object$ OriginalHost | Choices$ Creature.YouCtrl,Land.YouCtrl
|
||||
SVar:NonStackingAttachEffect:True
|
||||
Oracle:Enchant creature or land\nEnchanted permanent has "At the beginning of your end step, sacrifice this permanent and attach Nettlevine Blight to a creature or land you control."
|
||||
|
||||
@@ -2,6 +2,5 @@ Name:Nomad Mythmaker
|
||||
ManaCost:2 W
|
||||
Types:Creature Human Nomad Cleric
|
||||
PT:2/2
|
||||
A:AB$ ChangeZone | Cost$ W T | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Aura | GainControl$ True | AttachedTo$ Creature.CanBeEnchantedBySource+YouCtrl | SpellDescription$ Put target Aura card from a graveyard onto the battlefield under your control attached to a creature you control.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/nomad_mythmaker.jpg
|
||||
Oracle:{W}, {T}: Put target Aura card from a graveyard onto the battlefield under your control attached to a creature you control.
|
||||
A:AB$ ChangeZone | Cost$ W T | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Aura | GainControl$ True | AttachedTo$ Creature.YouCtrl | AILogic$ Pump | SpellDescription$ Put target Aura card from a graveyard onto the battlefield under your control attached to a creature you control.
|
||||
Oracle:{W}, {T}: Put target Aura card from a graveyard onto the battlefield under your control attached to a creature you control.
|
||||
@@ -2,11 +2,10 @@ Name:Quest for the Holy Relic
|
||||
ManaCost:W
|
||||
Types:Enchantment
|
||||
T:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigPutCounter | TriggerDescription$ Whenever you cast a creature spell, you may put a quest counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ QUEST | CounterNum$ 1 | SpellDescription$ Whenever you cast a creature spell, you may put a quest counter on CARDNAME.
|
||||
A:AB$ ChangeZone | Cost$ SubCounter<5/QUEST> Sac<1/CARDNAME> | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.Equipment+YouOwn | ChangeNum$ 1 | AttachedTo$ Creature.YouCtrl | SpellDescription$ Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ QUEST | CounterNum$ 1
|
||||
A:AB$ ChangeZone | Cost$ SubCounter<5/QUEST> Sac<1/CARDNAME> | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.Equipment+YouOwn | ChangeNum$ 1 | AttachAfter$ Creature.YouCtrl | SpellDescription$ Search your library for an Equipment card, put it onto the battlefield, and attach it to a creature you control. Then shuffle your library.
|
||||
AI:RemoveDeck:Random
|
||||
DeckNeeds:Type$Equipment
|
||||
DeckHas:Ability$Counters
|
||||
SVar:MaxQuestEffect:5
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/quest_for_the_holy_relic.jpg
|
||||
Oracle:Whenever you cast a creature spell, you may put a quest counter on Quest for the Holy Relic.\nRemove five quest counters from Quest for the Holy Relic and sacrifice it: Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.
|
||||
|
||||
@@ -4,10 +4,9 @@ Types:Enchantment Aura
|
||||
K:Enchant creature
|
||||
A:SP$ Attach | Cost$ 3 G | ValidTgts$ Creature | AILogic$ Pump
|
||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Enchanted creature gets +2/+2.
|
||||
T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy | Origin$ Battlefield | Destination$ Graveyard | OptionalDecider$ You | Execute$ DBReturnChoose | TriggerDescription$ When enchanted creature dies, you may return CARDNAME from your graveyard to the battlefield attached to a creature that shares a creature type with that creature.
|
||||
SVar:DBReturnChoose:DB$ ChooseCard | Choices$ Creature.CanBeEnchantedBy+sharesCreatureTypeWith TriggeredCardLKICopy | ChoiceTitle$ Choose a creature shares a creature type with the former enchanted creature | SubAbility$ DBReturn
|
||||
SVar:DBReturn:DB$ ChangeZone | Defined$ Self | Origin$ Graveyard | Destination$ Battlefield | AttachedTo$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||
T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy | Origin$ Battlefield | Destination$ Graveyard | OptionalDecider$ You | Execute$ DBReturn | TriggerDescription$ When enchanted creature dies, you may return CARDNAME from your graveyard to the battlefield attached to a creature that shares a creature type with that creature.
|
||||
#TODO CorrectedSelf might not be 100% correct, but there seems no other way
|
||||
SVar:DBReturn:DB$ ChangeZone | Defined$ CorrectedSelf | Origin$ Graveyard | Destination$ Battlefield | AttachedTo$ Creature.sharesCreatureTypeWith TriggeredCardLKICopy | AILogic$ Pump
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Enchant creature\nEnchanted creature gets +2/+2.\nWhen enchanted creature dies, you may return Reins of the Vinesteed from your graveyard to the battlefield attached to a creature that shares a creature type with that creature.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
Name:Retether
|
||||
ManaCost:3 W
|
||||
Types:Sorcery
|
||||
A:SP$ RepeatEach | Cost$ 3 W | RepeatCards$ Aura.YouOwn | Zone$ Graveyard | RepeatSubAbility$ DBAttach | SpellDescription$ Return each Aura card from your graveyard to the battlefield. Only creatures can be enchanted this way. (Aura cards that can't enchant a creature on the battlefield remain in your graveyard.)
|
||||
SVar:DBAttach:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Remembered | AttachedTo$ Creature.CanBeEnchantedByTargeted
|
||||
A:SP$ ChangeZone | Cost$ 3 W | Origin$ Graveyard | Destination$ Battlefield | Defined$ ValidGraveyard Aura.YouOwn | AttachedTo$ Creature | AILogic$ Pump | SpellDescription$ Return each Aura card from your graveyard to the battlefield. Only creatures can be enchanted this way. (Aura cards that can't enchant a creature on the battlefield remain in your graveyard.)
|
||||
AI:RemoveDeck:Random
|
||||
DeckNeeds:Type$Aura
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/retether.jpg
|
||||
SVar:NeedsToPlay:Creature.YouCtrl
|
||||
Oracle:Return each Aura card from your graveyard to the battlefield. Only creatures can be enchanted this way. (Aura cards that can't enchant a creature on the battlefield remain in your graveyard.)
|
||||
|
||||
@@ -3,10 +3,6 @@ ManaCost:GU GU
|
||||
Types:Creature Elf Wizard
|
||||
PT:2/2
|
||||
A:AB$ MoveCounter | Cost$ 1 G | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select target creatures to move +1/+1 counters | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Move a +1/+1 counter from target creature onto another target creature with the same controller.
|
||||
A:AB$ Pump | Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target aura to move | RememberObjects$ Valid Permanent.EnchantedBy Targeted | SubAbility$ ChooseNewHost | StackDescription$ None | SpellDescription$ Attach target Aura attached to a permanent to another permanent with the same controller.
|
||||
SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Permanent.NotEnchantedByTargeted+sharesControllerWith Remembered+CanBeEnchantedByTargeted | ChoiceZone$ Battlefield | SubAbility$ DBAttach | AILogic$ AtLeast1
|
||||
SVar:DBAttach:DB$ Attach | Object$ ParentTarget | Defined$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Attach | Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target aura to move | Object$ Targeted | Choices$ Permanent.sharesControllerWith AttachedBy Targeted | Move$ True | StackDescription$ SpellDescription | SpellDescription$ Attach target Aura attached to a permanent to another permanent with the same controller.
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/simic_guildmage.jpg
|
||||
Oracle:({G/U} can be paid with either {G} or {U}.)\n{1}{G}: Move a +1/+1 counter from target creature onto another target creature with the same controller.\n{1}{U}: Attach target Aura attached to a permanent to another permanent with the same controller.
|
||||
|
||||
@@ -5,12 +5,10 @@ K:Enchant instant card in a graveyard
|
||||
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Instant | TgtZone$ Graveyard | TgtPrompt$ Select target instant card in a graveyard | AILogic$ Pump
|
||||
T:Mode$ SpellCast | ValidCard$ Sorcery | ValidActivatingPlayer$ You | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost. If you do, exile the enchanted card and attach CARDNAME to another instant card in a graveyard.
|
||||
SVar:TrigCopy:DB$ Play | Defined$ Enchanted | ValidSA$ Spell | WithoutManaCost$ True | Optional$ True | CopyCard$ True | RememberPlayed$ True | SubAbility$ DBExile
|
||||
SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ Enchanted | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBChooseCard
|
||||
SVar:DBChooseCard:DB$ ChooseCard | Choices$ Instant | ChoiceZone$ Graveyard | Amount$ 1 | Mandatory$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Defined$ ChosenCard | Object$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
|
||||
SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ Enchanted | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Choices$ Instant | ChoiceZone$ Graveyard | Object$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Remembered$Amount
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/spellweaver_volute.jpg
|
||||
Oracle:Enchant instant card in a graveyard\nWhenever you cast a sorcery spell, copy the enchanted instant card. You may cast the copy without paying its mana cost. If you do, exile the enchanted card and attach Spellweaver Volute to another instant card in a graveyard.
|
||||
|
||||
@@ -5,9 +5,7 @@ K:Enchant land
|
||||
A:SP$ Attach | Cost$ 1 R R | ValidTgts$ Land | AILogic$ Curse
|
||||
T:Mode$ Taps | ValidCard$ Card.AttachedBy | Execute$ TrigDestroy | TriggerDescription$ When enchanted land becomes tapped, destroy it and CARDNAME deals 1 damage to that land's controller. That player attaches CARDNAME to a land of their choice.
|
||||
SVar:TrigDestroy:DB$ Destroy | Defined$ TriggeredCardLKICopy | SubAbility$ DBDmg
|
||||
SVar:DBDmg:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 1 | SubAbility$ DBChoose
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ TriggeredCardController | Choices$ Land | AILogic$ OppPreferred | Mandatory$ True | Amount$ 1 | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Object$ Self | Defined$ ChosenCard
|
||||
SVar:DBDmg:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 1 | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ Attach | Object$ Self | Chooser$ TriggeredCardController | Choices$ Land | AILogic$ Curse
|
||||
SVar:NonStackingAttachEffect:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/steam_vines.jpg
|
||||
Oracle:Enchant land\nWhen enchanted land becomes tapped, destroy it and Steam Vines deals 1 damage to that land's controller. That player attaches Steam Vines to a land of their choice.
|
||||
|
||||
@@ -3,10 +3,6 @@ ManaCost:3 W W
|
||||
Types:Creature Giant Warrior
|
||||
PT:4/4
|
||||
K:Vigilance
|
||||
A:AB$ ChangeZone | Cost$ 1 W T | Origin$ Library | Destination$ Battlefield | ChangeType$ Equipment | ChangeNum$ 1 | Imprint$ True | SubAbility$ DBChoose | SpellDescription$ Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature | SubAbility$ DBAttach | RememberChosen$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1
|
||||
SVar:DBAttach:DB$ Attach | Object$ Imprinted | Defined$ Remembered | SubAbility$ DBCleanup | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
|
||||
SVar:X:Imprinted$Amount
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/stonehewer_giant.jpg
|
||||
A:AB$ ChangeZone | Cost$ 1 W T | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.Equipment+YouOwn | ChangeNum$ 1 | AttachAfter$ Creature.YouCtrl | SpellDescription$ Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.
|
||||
DeckNeeds:Type$Equipment
|
||||
Oracle:Vigilance\n{1}{W}, {T}: Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Creature Human Shaman
|
||||
PT:3/2
|
||||
K:Haste
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ GraveAuras | TriggerDescription$ When CARDNAME enters the battlefield, return any number of Aura cards from your graveyard to the battlefield attached to creatures you control. Exile those Auras at the beginning of your next end step. If those Auras would leave the battlefield, exile them instead of putting them anywhere else.
|
||||
SVar:GraveAuras:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Aura.YouOwn | RememberChanged$ True | AttachedTo$ Creature.YouCtrl | ChangeNum$ GraveX | Optional$ True | Hidden$ True | SubAbility$ DBUnearthed
|
||||
SVar:GraveAuras:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Aura.YouOwn | RememberChanged$ True | AttachedTo$ Creature.YouCtrl | ChangeNum$ GraveX | Optional$ True | Hidden$ True | AILogic$ Pump | SubAbility$ DBUnearthed
|
||||
SVar:DBUnearthed:DB$ Animate | Defined$ Remembered | LeaveBattlefield$ Exile | Duration$ Permanent | SubAbility$ DelayedExile | StackDescription$ If those Auras would leave the battlefield, exile them instead of putting them anywhere else.
|
||||
SVar:DelayedExile:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | Execute$ TrigReturn | RememberObjects$ RememberedLKI | TriggerDescription$ Exile those Auras at the beginning of your next end step. | SubAbility$ DBCleanup
|
||||
SVar:TrigReturn:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ DelayTriggerRememberedLKI
|
||||
|
||||
@@ -4,7 +4,5 @@ Types:Creature Human Warrior
|
||||
PT:2/2
|
||||
K:Haste
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAttachAll | TriggerDescription$ When CARDNAME enters the battlefield, attach all Equipment on the battlefield to it. (Control of the Equipment doesn't change.)
|
||||
SVar:TrigAttachAll:DB$ RepeatEach | RepeatSubAbility$ DBAttach | RepeatCards$ Equipment | SpellDescription$ attach all Equipment on the battlefield to CARDNAME.
|
||||
SVar:DBAttach:DB$ Attach | Object$ Remembered | Defined$ TriggeredCard
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/vulshok_battlemaster.jpg
|
||||
Oracle:Haste\nWhen Vulshok Battlemaster enters the battlefield, attach all Equipment on the battlefield to it. (Control of the Equipment doesn't change.)
|
||||
SVar:TrigAttachAll:DB$ Attach | Object$ Valid Equipment | Defined$ TriggeredCard | SpellDescription$ attach all Equipment on the battlefield to CARDNAME.
|
||||
Oracle:Haste\nWhen Vulshok Battlemaster enters the battlefield, attach all Equipment on the battlefield to it. (Control of the Equipment doesn't change.)
|
||||
|
||||
@@ -44,7 +44,7 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
|
||||
getController().getGui().setSelectables(vCards);
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null;
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getLastKnownZone() : null;
|
||||
if (cz != null) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(), cz.getZoneType()));
|
||||
}
|
||||
|
||||
@@ -133,14 +133,14 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
getController().getGui().updateButtons(getOwner(), true, true, false);
|
||||
} else if (!sa.isMinTargetChosen() || (divisionValues != null && !divisionValues.isEmpty())){
|
||||
// If reached Minimum targets, enable OK button
|
||||
if (mandatory && tgt.hasCandidates(sa, true)) {
|
||||
if (mandatory && tgt.hasCandidates(sa)) {
|
||||
// Player has to click on a target
|
||||
getController().getGui().updateButtons(getOwner(), false, false, false);
|
||||
} else {
|
||||
getController().getGui().updateButtons(getOwner(), false, true, false);
|
||||
}
|
||||
} else {
|
||||
if (mandatory && tgt.hasCandidates(sa, true)) {
|
||||
if (mandatory && tgt.hasCandidates(sa)) {
|
||||
// Player has to click on a target or ok
|
||||
getController().getGui().updateButtons(getOwner(), true, false, true);
|
||||
} else {
|
||||
|
||||
@@ -2766,7 +2766,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
if (finalC.getRules().getType().isLand()) {
|
||||
// this is needed to ensure land abilities fire
|
||||
getGame().getAction().moveToHand(forgeCard, null);
|
||||
getGame().getAction().moveToPlay(forgeCard, null);
|
||||
getGame().getAction().moveToPlay(forgeCard, null, null);
|
||||
// ensure triggered abilities fire
|
||||
getGame().getTriggerHandler().runWaitingTriggers();
|
||||
} else {
|
||||
|
||||
@@ -102,7 +102,7 @@ public class TargetSelection {
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean hasCandidates = tgt.hasCandidates(this.ability, true);
|
||||
final boolean hasCandidates = tgt.hasCandidates(this.ability);
|
||||
if (!hasCandidates && !hasEnoughTargets) {
|
||||
// Cancel ability if there aren't any valid Candidates
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user