mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Codechanges and lots of fixes related to playing other player's cards from exile.
- Cards now keep track of who's allowed to cast them, fixing possible issues in multiplayer. - Face-down cards can now be properly looked at when allowed to by a static ability. - Improve support for casting a face-down exiled card in general. - Add Shared Fate (including AI support).
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -446,6 +446,7 @@ forge-game/src/main/java/forge/game/card/CardDamageHistory.java -text
|
||||
forge-game/src/main/java/forge/game/card/CardFactory.java svneol=native#text/plain
|
||||
forge-game/src/main/java/forge/game/card/CardFactoryUtil.java svneol=native#text/plain
|
||||
forge-game/src/main/java/forge/game/card/CardLists.java svneol=native#text/plain
|
||||
forge-game/src/main/java/forge/game/card/CardPlayOption.java -text
|
||||
forge-game/src/main/java/forge/game/card/CardPowerToughness.java svneol=native#text/plain
|
||||
forge-game/src/main/java/forge/game/card/CardPredicates.java svneol=native#text/plain
|
||||
forge-game/src/main/java/forge/game/card/CardShields.java -text
|
||||
@@ -12181,6 +12182,7 @@ forge-gui/res/cardsfolder/s/sharding_sphinx.txt svneol=native#text/plain
|
||||
forge-gui/res/cardsfolder/s/shardless_agent.txt -text
|
||||
forge-gui/res/cardsfolder/s/shared_animosity.txt -text
|
||||
forge-gui/res/cardsfolder/s/shared_discovery.txt svneol=native#text/plain
|
||||
forge-gui/res/cardsfolder/s/shared_fate.txt -text
|
||||
forge-gui/res/cardsfolder/s/shared_trauma.txt -text
|
||||
forge-gui/res/cardsfolder/s/shared_triumph.txt svneol=native#text/plain
|
||||
forge-gui/res/cardsfolder/s/sharpened_pitchfork.txt -text
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.CardType.Supertype;
|
||||
@@ -335,7 +336,7 @@ public class AiController {
|
||||
sa.setActivatingPlayer(player);
|
||||
//add alternative costs as additional spell abilities
|
||||
newAbilities.add(sa);
|
||||
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa));
|
||||
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||
}
|
||||
|
||||
final ArrayList<SpellAbility> result = new ArrayList<SpellAbility>();
|
||||
@@ -352,6 +353,11 @@ public class AiController {
|
||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||
spellAbilities.add(sa);
|
||||
}
|
||||
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && c.mayPlay(player) != null) {
|
||||
for (final SpellAbility sa : c.getState(CardStateName.Original).getSpellAbilities()) {
|
||||
spellAbilities.add(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
return spellAbilities;
|
||||
}
|
||||
@@ -392,11 +398,15 @@ public class AiController {
|
||||
});
|
||||
|
||||
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
||||
landsNotInHand.addAll(game.getCardsIn(ZoneType.Exile));
|
||||
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||
landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0));
|
||||
}
|
||||
for (final Card crd : landsNotInHand) {
|
||||
if (crd.isLand() && crd.hasKeyword("May be played")) {
|
||||
if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
|
||||
continue;
|
||||
}
|
||||
if (crd.hasKeyword("May be played") || crd.mayPlay(player) != null) {
|
||||
landList.add(crd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPlayOption;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
@@ -143,20 +144,37 @@ public final class GameActionUtil {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAlternativeCosts.
|
||||
* Find the alternative costs to a {@link SpellAbility}.
|
||||
* </p>
|
||||
*
|
||||
* @param sa
|
||||
* a SpellAbility.
|
||||
* @return an ArrayList<SpellAbility>.
|
||||
* get alternative costs as additional spell abilities
|
||||
* a {@link SpellAbility}.
|
||||
* @param activator
|
||||
* the {@link Player} for which to calculate available
|
||||
* @return a {@link List} of {@link SpellAbility} objects, each representing
|
||||
* a possible alternative cost the provided activator can use to pay
|
||||
* the provided {@link SpellAbility}.
|
||||
*/
|
||||
public static final ArrayList<SpellAbility> getAlternativeCosts(SpellAbility sa) {
|
||||
ArrayList<SpellAbility> alternatives = new ArrayList<SpellAbility>();
|
||||
Card source = sa.getHostCard();
|
||||
public static final List<SpellAbility> getAlternativeCosts(final SpellAbility sa, final Player activator) {
|
||||
final List<SpellAbility> alternatives = new ArrayList<SpellAbility>();
|
||||
if (!sa.isBasicSpell()) {
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
final CardPlayOption playOption = source.mayPlay(activator);
|
||||
if (sa.isSpell() && playOption != null && playOption.isWithoutManaCost()) {
|
||||
final SpellAbility newSA = sa.copy();
|
||||
final SpellAbilityRestriction sar = new SpellAbilityRestriction();
|
||||
sar.setVariables(sa.getRestrictions());
|
||||
sar.setZone(null);
|
||||
newSA.setRestrictions(sar);
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setPayCosts(newSA.getPayCosts().copyWithNoMana());
|
||||
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
for (final String keyword : source.getKeywords()) {
|
||||
if (sa.isSpell() && keyword.startsWith("Flashback")) {
|
||||
final SpellAbility flashback = sa.copy();
|
||||
@@ -183,18 +201,6 @@ public final class GameActionUtil {
|
||||
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
if (sa.isSpell() && keyword.equals("May be played by your opponent without paying its mana cost")) {
|
||||
final SpellAbility newSA = sa.copy();
|
||||
SpellAbilityRestriction sar = new SpellAbilityRestriction();
|
||||
sar.setVariables(sa.getRestrictions());
|
||||
sar.setZone(null);
|
||||
sar.setOpponentOnly(true);
|
||||
newSA.setRestrictions(sar);
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setPayCosts(newSA.getPayCosts().copyWithNoMana());
|
||||
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
if (sa.isSpell() && keyword.startsWith("May be played without paying its mana cost and as though it has flash")) {
|
||||
final SpellAbility newSA = sa.copy();
|
||||
SpellAbilityRestriction sar = new SpellAbilityRestriction();
|
||||
|
||||
@@ -92,7 +92,7 @@ public class StaticEffects {
|
||||
boolean setPT = false;
|
||||
String[] addHiddenKeywords = null;
|
||||
String addColors = null;
|
||||
boolean removeMayLookAt = false;
|
||||
boolean removeMayLookAt = false, removeMayPlay = false;
|
||||
|
||||
if (params.containsKey("ChangeColorWordsTo")) {
|
||||
changeColorWordsTo = params.get("ChangeColorWordsTo");
|
||||
@@ -158,6 +158,9 @@ public class StaticEffects {
|
||||
if (params.containsKey("MayLookAt")) {
|
||||
removeMayLookAt = true;
|
||||
}
|
||||
if (params.containsKey("MayPlay")) {
|
||||
removeMayPlay = true;
|
||||
}
|
||||
|
||||
if (params.containsKey("IgnoreEffectCost")) {
|
||||
for (final SpellAbility s : se.getSource().getSpellAbilities()) {
|
||||
@@ -254,6 +257,9 @@ public class StaticEffects {
|
||||
if (removeMayLookAt) {
|
||||
affectedCard.setMayLookAt(controller, false);
|
||||
}
|
||||
if (removeMayPlay) {
|
||||
affectedCard.removeMayPlay(controller);
|
||||
}
|
||||
}
|
||||
se.clearTimestamps();
|
||||
return affectedCards;
|
||||
|
||||
@@ -112,8 +112,6 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
eff.setImmutable(true);
|
||||
eff.setEffectSource(hostCard);
|
||||
|
||||
// Effects should be Orange or something probably
|
||||
|
||||
final Card e = eff;
|
||||
|
||||
// Grant SVars first in order to give references to granted abilities
|
||||
|
||||
@@ -136,6 +136,8 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
// if this card is an Aura, what Entity is it enchanting?
|
||||
private GameEntity enchanting = null;
|
||||
|
||||
private final Map<Player, CardPlayOption> mayPlay = Maps.newTreeMap();
|
||||
|
||||
// changes by AF animate and continuous static effects - timestamp is the key of maps
|
||||
private Map<Long, CardChangedType> changedCardTypes = new ConcurrentSkipListMap<Long, CardChangedType>();
|
||||
private Map<Long, KeywordsChange> changedCardKeywords = new ConcurrentSkipListMap<Long, KeywordsChange>();
|
||||
@@ -1503,10 +1505,22 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
public String getAbilityText() {
|
||||
return getAbilityText(currentState);
|
||||
}
|
||||
public String getAbilityText(CardState state) {
|
||||
public String getAbilityText(final CardState state) {
|
||||
final CardTypeView type = state.getType();
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (!mayPlay.isEmpty()) {
|
||||
sb.append("May be played by: ");
|
||||
sb.append(Lang.joinHomogenous(mayPlay.entrySet(), new Function<Entry<Player, CardPlayOption>, String>() {
|
||||
@Override public String apply(final Entry<Player, CardPlayOption> entry) {
|
||||
return entry.getKey().toString() + entry.getValue().toString();
|
||||
}
|
||||
}));
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
if (type.isInstant() || type.isSorcery()) {
|
||||
final StringBuilder sb = abilityTextInstantSorcery(state);
|
||||
sb.append(abilityTextInstantSorcery(state));
|
||||
|
||||
if (haunting != null) {
|
||||
sb.append("Haunting: ").append(haunting);
|
||||
@@ -1520,8 +1534,6 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
return sb.toString().replaceAll("CARDNAME", state.getName());
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (monstrous) {
|
||||
sb.append("Monstrous\r\n");
|
||||
}
|
||||
@@ -2141,6 +2153,17 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
view.setPlayerMayLook(player, mayLookAt, temp);
|
||||
}
|
||||
|
||||
public final CardPlayOption mayPlay(final Player player) {
|
||||
return mayPlay.get(player);
|
||||
}
|
||||
public final void setMayPlay(final Player player, final boolean withoutManaCost, final boolean ignoreColor) {
|
||||
final CardPlayOption option = this.mayPlay.get(player);
|
||||
this.mayPlay.put(player, option == null ? new CardPlayOption(withoutManaCost, ignoreColor) : option.add(withoutManaCost, ignoreColor));
|
||||
}
|
||||
public final void removeMayPlay(final Player player) {
|
||||
this.mayPlay.remove(player);
|
||||
}
|
||||
|
||||
public final CardCollectionView getEquippedBy(boolean allowModify) {
|
||||
return CardCollection.getView(equippedBy, allowModify);
|
||||
}
|
||||
@@ -6246,13 +6269,18 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<SpellAbility> getAllPossibleAbilities(Player player, boolean removeUnplayable) {
|
||||
public List<SpellAbility> getAllPossibleAbilities(final Player player, final boolean removeUnplayable) {
|
||||
// this can only be called by the Human
|
||||
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
|
||||
for (SpellAbility sa : getSpellAbilities()) {
|
||||
//add alternative costs as additional spell abilities
|
||||
abilities.add(sa);
|
||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa));
|
||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||
}
|
||||
if (isFaceDown() && isInZone(ZoneType.Exile) && mayPlay(player) != null) {
|
||||
for (final SpellAbility sa : getState(CardStateName.Original).getSpellAbilities()) {
|
||||
abilities.add(sa);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = abilities.size() - 1; i >= 0; i--) {
|
||||
@@ -6266,7 +6294,7 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
|
||||
}
|
||||
}
|
||||
|
||||
if (isLand() && player.canPlayLand(this)) {
|
||||
if (getState(CardStateName.Original).getType().isLand() && player.canPlayLand(this)) {
|
||||
Ability.PLAY_LAND_SURROGATE.setHostCard(this);
|
||||
abilities.add(Ability.PLAY_LAND_SURROGATE);
|
||||
}
|
||||
|
||||
36
forge-game/src/main/java/forge/game/card/CardPlayOption.java
Normal file
36
forge-game/src/main/java/forge/game/card/CardPlayOption.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package forge.game.card;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public final class CardPlayOption {
|
||||
private final boolean withoutManaCost, ignoreManaCostColor;
|
||||
|
||||
public CardPlayOption(final boolean withoutManaCost, final boolean ignoreManaCostColor) {
|
||||
this.withoutManaCost = withoutManaCost;
|
||||
this.ignoreManaCostColor = ignoreManaCostColor;
|
||||
}
|
||||
|
||||
public CardPlayOption add(final boolean withoutManaCost, final boolean ignoreManaCostColor) {
|
||||
return new CardPlayOption(isWithoutManaCost() || withoutManaCost, isIgnoreManaCostColor() || ignoreManaCostColor);
|
||||
}
|
||||
|
||||
public boolean isWithoutManaCost() {
|
||||
return withoutManaCost;
|
||||
}
|
||||
|
||||
public boolean isIgnoreManaCostColor() {
|
||||
return ignoreManaCostColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isWithoutManaCost()) {
|
||||
return " (without paying its mana cost)";
|
||||
}
|
||||
if (isIgnoreManaCostColor()) {
|
||||
return " (may spend mana as though it were mana of any color to cast it)";
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package forge.game.card;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardEdition;
|
||||
@@ -102,6 +105,9 @@ public class CardView extends GameEntityView {
|
||||
void updateZone(Card c) {
|
||||
set(TrackableProperty.Zone, c.getZone() == null ? null : c.getZone().getZoneType());
|
||||
}
|
||||
public boolean isInZone(final Iterable<ZoneType> zones) {
|
||||
return Iterables.contains(zones, getZone());
|
||||
}
|
||||
|
||||
public boolean isCloned() {
|
||||
return get(TrackableProperty.Cloned);
|
||||
@@ -397,11 +403,17 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
//if viewer is controlled by another player, also check if face can be shown to that player
|
||||
PlayerView mindSlaveMaster = viewer.getMindSlaveMaster();
|
||||
final PlayerView mindSlaveMaster = viewer.getMindSlaveMaster();
|
||||
if (mindSlaveMaster != null && canFaceDownBeShownTo(mindSlaveMaster)) {
|
||||
return true;
|
||||
}
|
||||
return !getController().isOpponentOf(viewer) || getCurrentState().getOpponentMayLook();
|
||||
if (isInZone(EnumSet.of(ZoneType.Battlefield, ZoneType.Stack, ZoneType.Sideboard)) && getController().equals(viewer)) {
|
||||
return true;
|
||||
}
|
||||
if (getController().isOpponentOf(viewer) && getCurrentState().getOpponentMayLook()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CardView getEquipping() {
|
||||
|
||||
@@ -1386,6 +1386,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
// Dakkon Blackblade Avatar will use a similar effect
|
||||
if (canPlayLand(land, ignoreZoneAndTiming)) {
|
||||
land.setController(this, 0);
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp();
|
||||
}
|
||||
game.getAction().moveTo(getZone(ZoneType.Battlefield), land);
|
||||
|
||||
// play a sound
|
||||
@@ -1407,7 +1410,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final boolean canPlayLand(final Card land) {
|
||||
return canPlayLand(land, false);
|
||||
}
|
||||
public final boolean canPlayLand(Card land, final boolean ignoreZoneAndTiming) {
|
||||
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming) {
|
||||
if (!ignoreZoneAndTiming && !canCastSorcery()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1423,14 +1426,13 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
|
||||
if (land != null && !ignoreZoneAndTiming) {
|
||||
if (land.getOwner() != this && !land.hasKeyword("May be played by your opponent"))
|
||||
return false;
|
||||
|
||||
if (land.getOwner() == this && land.hasKeyword("May be played by your opponent") && !land.hasKeyword("May be played"))
|
||||
final boolean mayPlay = land.mayPlay(this) != null;
|
||||
if (land.getOwner() != this && !mayPlay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Zone zone = game.getZoneOf(land);
|
||||
if (zone != null && (zone.is(ZoneType.Battlefield) || (!zone.is(ZoneType.Hand) && !land.hasStartOfKeyword("May be played")))) {
|
||||
if (zone != null && (zone.is(ZoneType.Battlefield) || (!zone.is(ZoneType.Hand) && !(mayPlay || land.hasStartOfKeyword("May be played"))))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package forge.game.spellability;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
@@ -91,7 +92,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
}
|
||||
|
||||
// for uncastables like lotus bloom, check if manaCost is blank (except for morph spells)
|
||||
if (!isCastFaceDown() && isBasicSpell() && card.getManaCost().isNoCost()) {
|
||||
if (!isCastFaceDown() && isBasicSpell() && card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost().isNoCost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,9 +200,8 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
return true;
|
||||
}
|
||||
|
||||
Player activator = sa.getActivatingPlayer();
|
||||
|
||||
Zone cardZone = activator.getGame().getZoneOf(c);
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Zone cardZone = activator.getGame().getZoneOf(c);
|
||||
if (cardZone == null || !cardZone.is(this.getZone())) {
|
||||
// If Card is not in the default activating zone, do some additional checks
|
||||
// Not a Spell, or on Battlefield, return false
|
||||
@@ -211,12 +210,12 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
return false;
|
||||
}
|
||||
if (cardZone.is(ZoneType.Stack)) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
if (c.hasKeyword("May be played") && activator.equals(c.getController())) {
|
||||
if (c.mayPlay(activator) != null) {
|
||||
return true;
|
||||
}
|
||||
if (c.hasKeyword("May be played by your opponent") && !activator.equals(c.getController())) {
|
||||
if (c.hasKeyword("May be played") && activator.equals(c.getController())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -296,7 +295,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sa.isSpell() && activator.isOpponentOf(c.getController()) && c.hasKeyword("May be played by your opponent")) {
|
||||
if (sa.isSpell() && c.mayPlay(activator) != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -257,9 +257,8 @@ public class StaticAbility extends CardTraitBase {
|
||||
return in;
|
||||
}
|
||||
|
||||
// apply the ability if it has the right mode
|
||||
/**
|
||||
* Apply ability.
|
||||
* Apply ability if it has the right mode.
|
||||
*
|
||||
* @param mode
|
||||
* the mode
|
||||
|
||||
@@ -113,6 +113,7 @@ public class StaticAbilityContinuous {
|
||||
boolean removeSubTypes = false;
|
||||
boolean removeCreatureTypes = false;
|
||||
boolean controllerMayLookAt = false;
|
||||
boolean controllerMayPlay = false, mayPlayWithoutManaCost = false, mayPlayIgnoreColor = false;
|
||||
|
||||
//Global rules changes
|
||||
if (params.containsKey("GlobalRule")) {
|
||||
@@ -328,6 +329,14 @@ public class StaticAbilityContinuous {
|
||||
if (params.containsKey("MayLookAt")) {
|
||||
controllerMayLookAt = true;
|
||||
}
|
||||
if (params.containsKey("MayPlay")) {
|
||||
controllerMayPlay = true;
|
||||
if (params.containsKey("MayPlayWithoutManaCost")) {
|
||||
mayPlayWithoutManaCost = true;
|
||||
} else if (params.containsKey("MayPlayIgnoreColor")) {
|
||||
mayPlayIgnoreColor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.containsKey("IgnoreEffectCost")) {
|
||||
String cost = params.get("IgnoreEffectCost");
|
||||
@@ -558,8 +567,11 @@ public class StaticAbilityContinuous {
|
||||
if (controllerMayLookAt) {
|
||||
affectedCard.setMayLookAt(controller, true);
|
||||
}
|
||||
if (controllerMayPlay) {
|
||||
affectedCard.setMayPlay(controller, mayPlayWithoutManaCost, mayPlayIgnoreColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return affectedCards;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,14 +39,17 @@ import forge.game.zone.ZoneType;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
public class TriggerHandler {
|
||||
private final ArrayList<TriggerType> suppressedModes = new ArrayList<TriggerType>();
|
||||
private final ArrayList<Trigger> activeTriggers = new ArrayList<Trigger>();
|
||||
private final List<TriggerType> suppressedModes = Collections.synchronizedList(new ArrayList<TriggerType>());
|
||||
private final List<Trigger> activeTriggers = Collections.synchronizedList(new ArrayList<Trigger>());
|
||||
|
||||
private final ArrayList<Trigger> delayedTriggers = new ArrayList<Trigger>();
|
||||
private final ArrayListMultimap<Player, Trigger> playerDefinedDelayedTriggers = ArrayListMultimap.create();
|
||||
private final List<TriggerWaiting> waitingTriggers = new ArrayList<TriggerWaiting>();
|
||||
private final List<Trigger> delayedTriggers = Collections.synchronizedList(new ArrayList<Trigger>());
|
||||
private final ListMultimap<Player, Trigger> playerDefinedDelayedTriggers = Multimaps.synchronizedListMultimap(ArrayListMultimap.<Player, Trigger>create());
|
||||
private final List<TriggerWaiting> waitingTriggers = Collections.synchronizedList(new ArrayList<TriggerWaiting>());
|
||||
private final Game game;
|
||||
|
||||
public TriggerHandler(Game gameState) {
|
||||
@@ -268,7 +271,7 @@ public class TriggerHandler {
|
||||
boolean checkStatics = false;
|
||||
|
||||
// Static triggers
|
||||
for (final Trigger t : activeTriggers) {
|
||||
for (final Trigger t : Lists.newArrayList(activeTriggers)) {
|
||||
if (t.isStatic() && canRunTrigger(t, mode, runParams)) {
|
||||
runSingleTrigger(t, runParams);
|
||||
checkStatics = true;
|
||||
|
||||
@@ -216,9 +216,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
|
||||
if (sp.isSpell()) {
|
||||
source.setController(activator, 0);
|
||||
Spell spell = (Spell) sp;
|
||||
final Spell spell = (Spell) sp;
|
||||
if (spell.isCastFaceDown()) {
|
||||
source.turnFaceDown();
|
||||
} else if (source.isFaceDown()) {
|
||||
source.turnFaceUp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ public enum TrackableProperty {
|
||||
ChosenDirection(TrackableTypes.EnumType(Direction.class)),
|
||||
Remembered(TrackableTypes.StringType),
|
||||
NamedCard(TrackableTypes.StringType),
|
||||
PlayerMayLook(TrackableTypes.PlayerViewCollectionType),
|
||||
PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType),
|
||||
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, false),
|
||||
PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType, false),
|
||||
Equipping(TrackableTypes.CardViewType),
|
||||
EquippedBy(TrackableTypes.CardViewCollectionType),
|
||||
Enchanting(TrackableTypes.GameEntityViewType),
|
||||
|
||||
@@ -7,9 +7,8 @@ T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage
|
||||
SVar:TrigExile:AB$ Mill | Cost$ 0 | Defined$ TriggeredTarget | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBGainLife
|
||||
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X | SubAbility$ DBEffect
|
||||
SVar:X:Remembered$CardManaCost
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay,STPlay2 | Triggers$ TriggerCastDoM | SVars$ TrigRemoveSelf | RememberObjects$ Remembered | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand+YouOwn | AddHiddenKeyword$ May be played & May spend mana as though it were mana of any color to cast CARDNAME | AffectedZone$ Exile | Description$ Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it.
|
||||
SVar:STPlay2:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand+OppOwn | AddHiddenKeyword$ May be played by your opponent & May spend mana as though it were mana of any color to cast CARDNAME | AffectedZone$ Exile
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | Triggers$ TriggerCastDoM | SVars$ TrigRemoveSelf | RememberObjects$ Remembered | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreColor$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it.
|
||||
SVar:TriggerCastDoM:Mode$ SpellCast | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Execute$ TrigRemoveSelf | Static$ True
|
||||
SVar:TrigRemoveSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
|
||||
@@ -4,8 +4,11 @@ Types:Creature Vampire Wizard
|
||||
PT:3/3
|
||||
K:Flying
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigChangeZone | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player exiles a card from his or her hand. You may play that card for as long as it remains exiled.
|
||||
SVar:TrigChangeZone:AB$ ChangeZone | Cost$ 0 | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TriggeredTarget | Chooser$ TriggeredTarget | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBPump
|
||||
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | Permanent$ True | SubAbility$ DBCleanup
|
||||
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TriggeredTarget | Chooser$ TriggeredTarget | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | Triggers$ TCleanup | RememberObjects$ Remembered | SVars$ DBExileSelf | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card for as long as it remains exiled.
|
||||
SVar:TCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | Execute$ DBExileSelf | Static$ True
|
||||
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Regenerate | Cost$ Sac<1/Human> | SpellDescription$ Regenerate CARDNAME.
|
||||
SVar:RemAIDeck:True
|
||||
|
||||
@@ -5,7 +5,8 @@ PT:4/4
|
||||
A:AB$ Pump | Cost$ 1 | ValidTgts$ Creature | TgtZone$ Graveyard | TgtPrompt$ Select target creature card | PumpZone$ Graveyard | SubAbility$ PumpYour | RememberObjects$ Targeted | SpellDescription$ You may cast target creature card in a graveyard this turn. When you cast that card this turn, CARDNAME gains all activated abilities of that card until end of turn.
|
||||
SVar:PumpYour:DB$ Pump | Defined$ Remembered | ConditionCheckSVar$ SelectKW | ConditionSVarCompare$ EQ1 | KW$ HIDDEN May be played | PumpZone$ Graveyard | SubAbility$ PumpTheir | References$ SelectKW
|
||||
SVar:PumpTheir:DB$ Pump | Defined$ Remembered | ConditionCheckSVar$ SelectKW | ConditionSVarCompare$ EQ0 | KW$ HIDDEN May be played by your opponent | PumpZone$ Graveyard | SubAbility$ FXCast | References$ SelectKW
|
||||
SVar:FXCast:DB$ Effect | Name$ Havengul Lich Delayed Trigger | Triggers$ DTCast | SVars$ StealAbs,STSteal,CleanupDT | RememberObjects$ Targeted
|
||||
SVar:FXCast:DB$ Effect | Name$ Havengul Lich Delayed Trigger | StaticAbilities$ STPlay | Triggers$ DTCast | SVars$ StealAbs,STSteal,CleanupDT | RememberObjects$ Targeted
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ Until end of turn, you may cast a creature card in a graveyard.
|
||||
SVar:DTCast:Mode$ SpellCast | ValidCard$ Card.IsRemembered | Execute$ StealAbs | TriggerDescription$ When you cast that card this turn, Havengul Lich gains all activated abilities of that card until end of turn.
|
||||
SVar:StealAbs:DB$ Effect | Name$ Havengul Lich effect | RememberObjects$ TriggeredCard | StaticAbilities$ STSteal | SubAbility$ CleanupDT
|
||||
SVar:STSteal:Mode$ Continuous | Affected$ EffectSource | EffectZone$ Command | GainsAbilitiesOf$ Creature.IsRemembered | GainsAbilitiesOfZones$ Library,Hand,Stack,Battlefield,Graveyard,Exile,Command
|
||||
|
||||
@@ -5,9 +5,8 @@ PT:3/3
|
||||
K:Morph:4 U U
|
||||
T:Mode$ TurnFaceUp | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME is turned face up, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled.
|
||||
SVar:TrigCounter:AB$ Counter | Cost$ 0 | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | RememberCountered$ True | ForgetOtherTargets$ True | Destination$ Exile | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | Name$ Kheru Spellsnatcher Effect | RememberObjects$ Remembered | StaticAbilities$ PlayOpp,PlayYou | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ PlayOpp,PlayYou,TrigCleanup,DBCleanup
|
||||
SVar:PlayOpp:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent without paying its mana cost | Description$ You may play cards exiled with Kheru Spellsnatcher.
|
||||
SVar:PlayYou:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+YouOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played without paying its mana cost
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ PlayOpp,PlayYou,TrigCleanup,DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may cast cards without paying their mana cost as long as they remain exiled.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True
|
||||
SVar:DBCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:RemAIDeck:True
|
||||
|
||||
@@ -3,9 +3,12 @@ ManaCost:3 U
|
||||
Types:Creature Faerie Rogue
|
||||
PT:1/4
|
||||
K:Flying
|
||||
A:AB$ Mill | Cost$ 1 U Q | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBPump | SpellDescription$ Target opponent exiles the top card of his or her library. Until end of turn, you may play that card.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Mill | Cost$ 1 U Q | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of his or her library. Until end of turn, you may play that card.
|
||||
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
|
||||
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ Truek
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/knacksaw_clique.jpg
|
||||
Oracle:Flying\n{1}{U}, {Q}: Target opponent exiles the top card of his or her library. Until end of turn, you may play that card. ({Q} is the untap symbol.)
|
||||
|
||||
@@ -2,9 +2,11 @@ Name:Muse Vessel
|
||||
ManaCost:4
|
||||
Types:Artifact
|
||||
A:AB$ ChangeZone | Cost$ 3 T | ValidTgts$ Player | TgtPrompt$ Select target player | SorcerySpeed$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | Chooser$ Targeted | Hidden$ True | IsCurse$ True | Mandatory$ True | SpellDescription$ Target player exiles a card from his or her hand. Activate this ability only any time you could cast a sorcery.
|
||||
A:AB$ ChooseCard | Cost$ 1 | ChoiceZone$ Exile | Choices$ Card.IsRemembered | Amount$ 1 | ChoiceTitle$ Choose a card exiled with Muse Vessel | SubAbility$ DBPlayYouOwn | AILogic$ Never | SpellDescription$ Choose a card exiled with CARDNAME. You may play that card this turn.
|
||||
SVar:DBPlayYouOwn:DB$ Pump | Defined$ ChosenCard | KW$ HIDDEN May be played | PumpZone$ Exile | ConditionDefined$ ChosenCard | ConditionPresent$ Card.YouOwn | ConditionCompare$ EQ1 | SubAbility$ DBPlayOppOwn
|
||||
SVar:DBPlayOppOwn:DB$ Pump | Defined$ ChosenCard | KW$ HIDDEN May be played by your opponent | PumpZone$ Exile | ConditionDefined$ ChosenCard | ConditionPresent$ Card.OppOwn | ConditionCompare$ EQ1
|
||||
A:AB$ ChooseCard | Cost$ 1 | ChoiceZone$ Exile | Choices$ Card.IsRemembered | Amount$ 1 | ChoiceTitle$ Choose a card exiled with Muse Vessel | SubAbility$ DBEffect | AILogic$ Never | SpellDescription$ Choose a card exiled with CARDNAME. You may play that card this turn.
|
||||
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | RememberObjects$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
|
||||
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ DBForget
|
||||
SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
|
||||
|
||||
@@ -5,8 +5,7 @@ PT:2/3
|
||||
K:Flying
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigMill | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player exiles the top card of his or her library.
|
||||
SVar:TrigMill:AB$ Mill | Cost$ 0 | Defined$ TriggeredTarget | NumCards$ 1 | Destination$ Exile | RememberMilled$ True
|
||||
S:Mode$ Continuous | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent | Description$ You may play cards exiled with CARDNAME.
|
||||
S:Mode$ Continuous | Affected$ Card.IsRemembered+YouOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played
|
||||
S:Mode$ Continuous | MayPlay$ True | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | Description$ You may play cards exiled with CARDNAME.
|
||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ DBForget
|
||||
SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
Name:Ornate Kanzashi
|
||||
ManaCost:5
|
||||
Types:Artifact
|
||||
A:AB$ Mill | Cost$ 2 T | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBPump | SpellDescription$ Target opponent exiles the top card of his or her library. You may play that card this turn.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | SubAbility$ DBCleanup
|
||||
A:AB$ Mill | Cost$ 2 T | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of his or her library. You may play that card this turn.
|
||||
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
|
||||
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/ornate_kanzashi.jpg
|
||||
|
||||
@@ -6,9 +6,9 @@ SVar:DBChangeZone:DB$ ChangeZoneAll | ChangeType$ Instant.IsRemembered,Sorcery.I
|
||||
SVar:DBForgetOther:DB$ Cleanup | ClearRemembered$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ0 | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ MischiefPlay | Triggers$ TrigEOT,TrigChangesZone | SVars$ MischiefCleanup,MischiefReturn | RememberObjects$ Remembered | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:MischiefPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent without paying its mana cost | Description$ You may play that exiled card.
|
||||
SVar:MischiefPlay:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | Description$ You may cast a card without paying its mana cost as long as it remains exiled.
|
||||
SVar:TrigEOT:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Command | Execute$ MischiefReturn | TriggerDescription$ At the beginning of the next end step, if you haven't cast it, return it to its owner's hand.
|
||||
SVar:MischiefReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand |
|
||||
SVar:MischiefReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand
|
||||
SVar:TrigChangesZone:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ MischiefCleanup
|
||||
SVar:MischiefCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:X:Count$ValidExile Instant.IsRemembered,Sorcery.IsRemembered
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
Name:Praetor's Grasp
|
||||
ManaCost:1 B B
|
||||
Types:Sorcery
|
||||
A:SP$ ChangeZone | Cost$ 1 B B | Origin$ Library | Destination$ Exile | ValidTgts$ Opponent | ChangeType$ Card | ChangeNum$ 1 | IsCurse$ True | RememberChanged$ True | SubAbility$ DBPump | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ Your opponent may look at this card. & May be played by your opponent | PumpZone$ Exile | Permanent$ True
|
||||
A:SP$ ChangeZone | Cost$ 1 B B | Origin$ Library | Destination$ Exile | ExileFaceDown$ True | ValidTgts$ Opponent | ChangeType$ Card | ChangeNum$ 1 | IsCurse$ True | RememberChanged$ True | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.
|
||||
SVar:DBEffect:DB$ Effect | MayLookAt$ True | MayPlay$ True | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
|
||||
SVar:STPlay:Mode$ Continuous | MayLookAt$ True | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play a card as long as it remains exiled.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
|
||||
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/praetors_grasp.jpg
|
||||
Oracle:Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.
|
||||
|
||||
18
forge-gui/res/cardsfolder/s/shared_fate.txt
Normal file
18
forge-gui/res/cardsfolder/s/shared_fate.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Name:Shared Fate
|
||||
ManaCost:4 U
|
||||
Types:Enchantment
|
||||
Text:If a player would draw a card, that player exiles the top card of an opponent's library face down instead. Each player may look at and play cards he or she exiled with CARDNAME.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEffects | Static$ True
|
||||
#Create an effect for each player. The effect contains both Shared Fate's abilities.
|
||||
SVar:TrigEffects:DB$ RepeatEach | RepeatPlayers$ Each | RepeatSubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | EffectOwner$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | ReplacementEffects$ RDraw | SVars$ DBChooseOpp,DBExile,DBCleanup | Duration$ UntilHostLeavesPlay
|
||||
SVar:RDraw:Event$ Draw | ActiveZones$ Command | ValidPlayer$ You | ReplaceWith$ DBChooseOpp | Description$ If you would draw a card, exile the top card of an opponent's library face down instead.
|
||||
SVar:DBChooseOpp:DB$ ChoosePlayer | ChoiceTitle$ Choose an opponent whose top library card to exile | Choices$ Player.Opponent | AILogic$ Curse | SubAbility$ DBExile
|
||||
SVar:DBExile:DB$ Mill | NumCards$ 1 | Destination$ Exile | ExileFaceDown$ True | Defined$ Player.Chosen | RememberMilled$ True
|
||||
SVar:STPlay:Mode$ Continuous | MayLookAt$ True | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play cards exiled with Shared Fate.
|
||||
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True
|
||||
SVar:DBCleanup:DB$ Cleanup | ForgetDefined$ TriggeredCard
|
||||
SVar:RemAIDeck:True
|
||||
SVar:RemRandomDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/shared_fate.jpg
|
||||
Oracle:If a player would draw a card, that player exiles the top card of an opponent's library face down instead.\nEach player may look at and play cards he or she exiled with Shared Fate.
|
||||
@@ -17,14 +17,20 @@
|
||||
*/
|
||||
package forge.player;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardPlayOption;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.cost.CostPayment;
|
||||
import forge.game.mana.ManaPool;
|
||||
@@ -37,10 +43,6 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.util.FCollection;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* SpellAbility_Requirements class.
|
||||
@@ -66,17 +68,26 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
// used to rollback
|
||||
Zone fromZone = null;
|
||||
CardStateName fromState = null;
|
||||
int zonePosition = 0;
|
||||
final ManaPool manapool = human.getManaPool();
|
||||
|
||||
final Card c = ability.getHostCard();
|
||||
boolean manaConversion = (ability.isSpell() && c.hasKeyword("May spend mana as though it were mana of any color to cast CARDNAME"));
|
||||
final CardPlayOption option = c.mayPlay(human);
|
||||
|
||||
boolean manaConversion = (ability.isSpell() && (c.hasKeyword("May spend mana as though it were mana of any color to cast CARDNAME")
|
||||
|| (option != null && option.isIgnoreManaCostColor())));
|
||||
boolean playerManaConversion = human.hasManaConversion()
|
||||
&& human.getController().confirmAction(ability, null, "Do you want to spend mana as though it were mana of any color to pay the cost?");
|
||||
if (ability instanceof Spell && !c.isCopiedSpell()) {
|
||||
fromZone = game.getZoneOf(c);
|
||||
fromState = c.getCurrentStateName();
|
||||
if (fromZone != null) {
|
||||
zonePosition = fromZone.getCards().indexOf(c);
|
||||
zonePosition = fromZone.getCards().indexOf(c);
|
||||
}
|
||||
// Turn face-down card face up (except case of morph spell)
|
||||
if (ability instanceof Spell && !((Spell) ability).isCastFaceDown() && fromState == CardStateName.FaceDown) {
|
||||
c.turnFaceUp();
|
||||
}
|
||||
ability.setHostCard(game.getAction().moveToStack(c));
|
||||
}
|
||||
@@ -100,7 +111,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (!prerequisitesMet) {
|
||||
if (!ability.isTrigger()) {
|
||||
rollbackAbility(fromZone, zonePosition);
|
||||
rollbackAbility(fromZone, fromState, zonePosition);
|
||||
if (ability.getHostCard().isMadness()) {
|
||||
// if a player failed to play madness cost, move the card to graveyard
|
||||
game.getAction().moveToGraveyard(c);
|
||||
@@ -125,8 +136,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (skipStack) {
|
||||
AbilityUtils.resolve(ability);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
enusureAbilityHasDescription(ability);
|
||||
game.getStack().addAndUnfreeze(ability);
|
||||
}
|
||||
@@ -182,13 +192,14 @@ public class HumanPlaySpellAbility {
|
||||
}
|
||||
}
|
||||
|
||||
private void rollbackAbility(Zone fromZone, int zonePosition) {
|
||||
private void rollbackAbility(final Zone fromZone, final CardStateName fromState, final int zonePosition) {
|
||||
// cancel ability during target choosing
|
||||
final Game game = ability.getActivatingPlayer().getGame();
|
||||
|
||||
if (fromZone != null) { // and not a copy
|
||||
// add back to where it came from
|
||||
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null);
|
||||
ability.getHostCard().setState(fromState, true);
|
||||
}
|
||||
|
||||
clearTargets(ability);
|
||||
@@ -255,7 +266,7 @@ public class HumanPlaySpellAbility {
|
||||
for (String aVar : announce.split(",")) {
|
||||
String varName = aVar.trim();
|
||||
if ("CreatureType".equals(varName)) {
|
||||
String choice = pc.chooseSomeType("Creature", ability, CardType.Constant.CREATURE_TYPES, new ArrayList<String>());
|
||||
final String choice = pc.chooseSomeType("Creature", ability, CardType.Constant.CREATURE_TYPES, Collections.<String>emptyList());
|
||||
ability.getHostCard().setChosenType(choice);
|
||||
}
|
||||
if ("ChooseNumber".equals(varName)) {
|
||||
|
||||
Reference in New Issue
Block a user