Player keyword rework

This commit is contained in:
Hans Mackowiak
2019-10-15 11:12:20 +00:00
parent 173c9ac5f6
commit 06b9dffa55
15 changed files with 317 additions and 114 deletions

View File

@@ -228,7 +228,7 @@ public class GameCopier {
CardFactory.copyCopiableCharacteristics(c, result); CardFactory.copyCopiableCharacteristics(c, result);
return result; return result;
} }
if (USE_FROM_PAPER_CARD && !c.isEmblem()) { if (USE_FROM_PAPER_CARD && !c.isEmblem() && c.getPaperCard() != null) {
Card newCard = Card.fromPaperCard(c.getPaperCard(), newOwner); Card newCard = Card.fromPaperCard(c.getPaperCard(), newOwner);
newCard.setCommander(c.isCommander()); newCard.setCommander(c.isCommander());
return newCard; return newCard;

View File

@@ -120,7 +120,10 @@ public class PumpEffect extends SpellAbilityEffect {
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) { && !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
return; return;
} }
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
if (!keywords.isEmpty()) {
p.addChangedKeywords(keywords, ImmutableList.of(), timestamp);
}
if (!sa.hasParam("Permanent")) { if (!sa.hasParam("Permanent")) {
// If not Permanent, remove Pumped at EOT // If not Permanent, remove Pumped at EOT
@@ -129,12 +132,7 @@ public class PumpEffect extends SpellAbilityEffect {
@Override @Override
public void run() { public void run() {
p.removeChangedKeywords(timestamp);
if (keywords.size() > 0) {
for (int i = 0; i < keywords.size(); i++) {
p.removeKeyword(keywords.get(i));
}
}
} }
}; };
addUntilCommand(sa, untilEOT); addUntilCommand(sa, untilEOT);

View File

@@ -5618,34 +5618,7 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final MutableBoolean result = new MutableBoolean(true);
visitKeywords(currentState, new Visitor<KeywordInterface>() {
@Override
public boolean visit(KeywordInterface kw) {
switch (kw.getOriginal()) {
case "Shroud":
StringBuilder sb = new StringBuilder();
sb.append("Can target CardUID_").append(getId());
sb.append(" with spells and abilities as though it didn't have shroud.");
if (sa.getActivatingPlayer() == null) {
System.err.println("Unexpected behavior: SA activator was null when trying to determine if the activating player could target a card with Shroud. SA host card = " + source + ", SA = " + sa);
result.setFalse(); // FIXME: maybe this should check by SA host card controller at this point instead?
} else if (!sa.getActivatingPlayer().hasKeyword(sb.toString())) {
result.setFalse();
}
break;
case "CARDNAME can't be the target of spells.":
if (sa.isSpell()) {
result.setFalse();
}
break;
}
return result.isTrue();
}
});
if (result.isFalse()) {
return false;
}
if (sa.isSpell()) { if (sa.isSpell()) {
for(KeywordInterface inst : source.getKeywords()) { for(KeywordInterface inst : source.getKeywords()) {
String kw = inst.getOriginal(); String kw = inst.getOriginal();

View File

@@ -4501,7 +4501,9 @@ public class CardFactoryUtil {
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True" effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
+ sbValid.toString() + " | Activator$ Opponent | Description$ " + sbValid.toString() + " | Activator$ Opponent | Description$ "
+ sbDesc.toString() + " (" + inst.getReminderText() + ")"; + sbDesc.toString() + " (" + inst.getReminderText() + ")";
} else if (keyword.equals("Shroud")) {
effect = "Mode$ CantTarget | Shroud$ True | ValidCard$ Card.Self | Secondary$ True"
+ " | Description$ Shroud (" + inst.getReminderText() + ")";
} else if (keyword.startsWith("Strive")) { } else if (keyword.startsWith("Strive")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final String manacost = k[1]; final String manacost = k[1];

View File

@@ -9,6 +9,8 @@ import com.google.common.collect.Lists;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactoryUtil; import forge.game.card.CardFactoryUtil;
import forge.game.player.Player;
import forge.game.player.PlayerFactoryUtil;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
@@ -82,7 +84,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public final void createTraits(final Card host, final boolean intrinsic) { public final void createTraits(final Card host, final boolean intrinsic) {
createTraits(host, intrinsic, false); createTraits(host, intrinsic, false);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.card.Card, boolean, boolean) * @see forge.game.keyword.KeywordInterface#createTraits(forge.game.card.Card, boolean, boolean)
@@ -125,6 +127,53 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
} }
} }
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.player.Player)
*/
@Override
public void createTraits(Player player) {
createTraits(player, false);
}
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.player.Player, boolean)
*/
@Override
public void createTraits(Player player, boolean clear) {
if (clear) {
triggers.clear();
replacements.clear();
abilities.clear();
staticAbilities.clear();
}
try {
String msg = "KeywordInstance:createTraits: make Traits for Keyword";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Player", player.getName()).withData("Keyword", this.original).build()
);
// add Extra for debugging
Sentry.getContext().addExtra("Player", player);
Sentry.getContext().addExtra("Keyword", this.original);
PlayerFactoryUtil.addTriggerAbility(this, player);
PlayerFactoryUtil.addReplacementEffect(this, player);
PlayerFactoryUtil.addSpellAbility(this, player);
PlayerFactoryUtil.addStaticAbility(this, player);
} catch (Exception e) {
String msg = "KeywordInstance:createTraits: failed Traits for Keyword";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Player", player.getName()).withData("Keyword", this.original).build()
);
//rethrow
throw new RuntimeException("Error in Keyword " + this.original + " for player " + player.getName(), e);
} finally {
// remove added extra
Sentry.getContext().removeExtra("Player");
Sentry.getContext().removeExtra("Keyword");
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#addTrigger(forge.game.trigger.Trigger) * @see forge.game.keyword.KeywordInterface#addTrigger(forge.game.trigger.Trigger)

View File

@@ -3,6 +3,7 @@ package forge.game.keyword;
import java.util.Collection; import java.util.Collection;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
@@ -24,6 +25,9 @@ public interface KeywordInterface extends Cloneable {
void createTraits(final Card host, final boolean intrinsic); void createTraits(final Card host, final boolean intrinsic);
void createTraits(final Card host, final boolean intrinsic, final boolean clear); void createTraits(final Card host, final boolean intrinsic, final boolean clear);
void createTraits(final Player player);
void createTraits(final Player player, final boolean clear);
void addTrigger(final Trigger trg); void addTrigger(final Trigger trg);
void addReplacement(final ReplacementEffect trg); void addReplacement(final ReplacementEffect trg);

View File

@@ -23,6 +23,11 @@ import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
/** /**
* <p> * <p>
@@ -131,7 +136,13 @@ public class KeywordsChange implements Cloneable {
inst.createTraits(host, false, true); inst.createTraits(host, false, true);
} }
} }
public final void addKeywordsToPlayer(final Player player) {
for (KeywordInterface inst : keywords.getValues()) {
inst.createTraits(player, true);
}
}
public final boolean removeKeywordfromAdd(final String keyword) { public final boolean removeKeywordfromAdd(final String keyword) {
return keywords.remove(keyword); return keywords.remove(keyword);
} }
@@ -201,6 +212,47 @@ public class KeywordsChange implements Cloneable {
} }
} }
/**
* @return the triggers
*/
public Collection<Trigger> getTriggers() {
List<Trigger> result = Lists.newArrayList();
for (KeywordInterface k : this.keywords.getValues()) {
result.addAll(k.getTriggers());
}
return result;
}
/**
* @return the replacements
*/
public Collection<ReplacementEffect> getReplacements() {
List<ReplacementEffect> result = Lists.newArrayList();
for (KeywordInterface k : this.keywords.getValues()) {
result.addAll(k.getReplacements());
}
return result;
}
/**
* @return the abilities
*/
public Collection<SpellAbility> getAbilities() {
List<SpellAbility> result = Lists.newArrayList();
for (KeywordInterface k : this.keywords.getValues()) {
result.addAll(k.getAbilities());
}
return result;
}
/**
* @return the staticAbilities
*/
public Collection<StaticAbility> getStaticAbilities() {
List<StaticAbility> result = Lists.newArrayList();
for (KeywordInterface k : this.keywords.getValues()) {
result.addAll(k.getStaticAbilities());
}
return result;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */

View File

@@ -20,6 +20,8 @@ package forge.game.player;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.ImageKeys;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.game.*; import forge.game.*;
@@ -41,7 +43,6 @@ import forge.game.phase.PhaseType;
import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementResult; import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType; import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilityActivated;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
@@ -155,6 +156,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private Card monarchEffect = null; private Card monarchEffect = null;
private Card blessingEffect = null; private Card blessingEffect = null;
private Card keywordEffect = null;
private final AchievementTracker achievementTracker = new AchievementTracker(); private final AchievementTracker achievementTracker = new AchievementTracker();
private final PlayerView view; private final PlayerView view;
@@ -1025,23 +1027,25 @@ public class Player extends GameEntity implements Comparable<Player> {
public final void addChangedKeywords(final List<String> addKeywords, final List<String> removeKeywords, final Long timestamp) { public final void addChangedKeywords(final List<String> addKeywords, final List<String> removeKeywords, final Long timestamp) {
// if the key already exists - merge entries // if the key already exists - merge entries
KeywordsChange cks = null;
if (changedKeywords.containsKey(timestamp)) { if (changedKeywords.containsKey(timestamp)) {
final KeywordsChange cks = changedKeywords.get(timestamp); getKeywordCard().removeChangedCardTraits(timestamp);
changedKeywords.put(timestamp, cks.merge(addKeywords, removeKeywords, cks = changedKeywords.get(timestamp).merge(addKeywords, removeKeywords, false, false);
cks.isRemoveAllKeywords(), cks.isRemoveIntrinsicKeywords())); } else {
updateKeywords(); cks = new KeywordsChange(addKeywords, removeKeywords, false, false);
return;
} }
cks.addKeywordsToPlayer(this);
changedKeywords.put(timestamp, new KeywordsChange(addKeywords, removeKeywords, false, false)); getKeywordCard().addChangedCardTraits(cks.getAbilities(), null, cks.getTriggers(), cks.getReplacements(), cks.getStaticAbilities(), false, false, false, timestamp);
changedKeywords.put(timestamp, cks);
updateKeywords(); updateKeywords();
game.fireEvent(new GameEventPlayerStatsChanged(this)); game.fireEvent(new GameEventPlayerStatsChanged(this));
} }
public final KeywordsChange removeChangedKeywords(final Long timestamp) { public final KeywordsChange removeChangedKeywords(final Long timestamp) {
KeywordsChange change = changedKeywords.remove(Long.valueOf(timestamp)); KeywordsChange change = changedKeywords.remove(timestamp);
if (change != null) { if (change != null) {
getKeywordCard().removeChangedCardTraits(timestamp);
updateKeywords(); updateKeywords();
game.fireEvent(new GameEventPlayerStatsChanged(this)); game.fireEvent(new GameEventPlayerStatsChanged(this));
} }
@@ -1100,6 +1104,7 @@ public class Player extends GameEntity implements Comparable<Player> {
for (final Entry<Long, KeywordsChange> ck : ImmutableList.copyOf(changedKeywords.entrySet())) { for (final Entry<Long, KeywordsChange> ck : ImmutableList.copyOf(changedKeywords.entrySet())) {
if (ck.getValue().isEmpty() && changedKeywords.remove(ck.getKey()) != null) { if (ck.getValue().isEmpty() && changedKeywords.remove(ck.getKey()) != null) {
keywordRemoved = true; keywordRemoved = true;
getKeywordCard().removeChangedCardTraits(ck.getKey());
} }
} }
@@ -1178,33 +1183,17 @@ public class Player extends GameEntity implements Comparable<Player> {
@Override @Override
public final boolean canBeTargetedBy(final SpellAbility sa) { public final boolean canBeTargetedBy(final SpellAbility sa) {
if (hasKeyword("Shroud")) {
return false; // CantTarget static abilities
} for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
if (hasKeyword("Hexproof")) { for (final StaticAbility stAb : ca.getStaticAbilities()) {
final Player a = sa.getActivatingPlayer(); if (stAb.applyAbility("CantTarget", this, sa)) {
if (isOpponentOf(a)) {
boolean cancelHexproof = false;
for (String k : a.getKeywords()) {
if (k.startsWith("IgnoreHexproof")) {
String[] m = k.split(":");
if (isValid(m[1].split(","), a, sa.getHostCard(), sa)) {
cancelHexproof = true;
break;
}
}
}
if (!cancelHexproof) {
return false; return false;
} }
} }
} }
if (hasProtectionFrom(sa.getHostCard())) { return !hasProtectionFrom(sa.getHostCard());
return false;
}
return (!hasKeyword("You can't be the targets of spells or activated abilities") || (!sa.isSpell() && (!(sa instanceof AbilityActivated))));
} }
@@ -2971,4 +2960,26 @@ public class Player extends GameEntity implements Comparable<Player> {
|| !hasKeyword("Spells and abilities you control can't cause you to search your library."); || !hasKeyword("Spells and abilities you control can't cause you to search your library.");
} }
public Card getKeywordCard() {
if (keywordEffect != null) {
return keywordEffect;
}
final PlayerZone com = getZone(ZoneType.Command);
keywordEffect = new Card(game.nextCardId(), null, false, game);
keywordEffect.setImmutable(true);
keywordEffect.setOwner(this);
keywordEffect.setName("Keyword Effects");
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
keywordEffect.addType("Effect");
keywordEffect.updateStateForView();
com.add(keywordEffect);
this.updateZoneForView(com);
return keywordEffect;
}
} }

View File

@@ -0,0 +1,46 @@
package forge.game.player;
import forge.game.card.Card;
import forge.game.keyword.KeywordInterface;
import forge.game.staticability.StaticAbility;
public class PlayerFactoryUtil {
public static void addStaticAbility(final KeywordInterface inst, final Player player) {
final Card card = player.getKeywordCard();
String keyword = inst.getOriginal();
String effect = null;
if (keyword.startsWith("Hexproof")) {
final StringBuilder sbDesc = new StringBuilder("Hexproof");
final StringBuilder sbValid = new StringBuilder();
if (!keyword.equals("Hexproof")) {
final String[] k = keyword.split(":");
sbDesc.append(" from ").append(k[2]);
sbValid.append("| ValidSource$ ").append(k[1]);
}
effect = "Mode$ CantTarget | Hexproof$ True | ValidPlayer$ Player.You | Secondary$ True "
+ sbValid.toString() + " | Activator$ Opponent | EffectZone$ Command | Description$ "
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
} else if (keyword.equals("Shroud")) {
effect = "Mode$ CantTarget | Shroud$ True | ValidPlayer$ Player.You | Secondary$ True "
+ "| EffectZone$ Command | Description$ Shroud (" + inst.getReminderText() + ")";
}
if (effect != null) {
StaticAbility st = new StaticAbility(effect, card);
st.setIntrinsic(false);
inst.addStaticAbility(st);
}
}
public static void addTriggerAbility(final KeywordInterface inst, Player player) {
}
public static void addReplacementEffect(final KeywordInterface inst, Player player) {
}
public static void addSpellAbility(final KeywordInterface inst, Player player) {
}
}

View File

@@ -391,6 +391,23 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
return false; return false;
} }
public final boolean applyAbility(final String mode, final Player player, final SpellAbility spellAbility) {
// don't apply the ability if it hasn't got the right mode
if (!getParam("Mode").equals(mode)) {
return false;
}
if (this.isSuppressed() || !this.checkConditions()) {
return false;
}
if (mode.equals("CantTarget")) {
return StaticAbilityCantTarget.applyCantTargetAbility(this, player, spellAbility);
}
return false;
}
public final boolean applyAbility(String mode, Card card, CounterType type) { public final boolean applyAbility(String mode, Card card, CounterType type) {
// don't apply the ability if it hasn't got the right mode // don't apply the ability if it hasn't got the right mode

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -22,8 +22,6 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import java.util.Map;
/** /**
* The Class StaticAbilityCantTarget. * The Class StaticAbilityCantTarget.
*/ */
@@ -31,8 +29,8 @@ public class StaticAbilityCantTarget {
/** /**
* Apply can't target ability. * Apply can't target ability.
* *
* @param staticAbility * @param st
* the static ability * the static ability
* @param card * @param card
* the card * the card
@@ -40,16 +38,19 @@ public class StaticAbilityCantTarget {
* the spell/ability * the spell/ability
* @return true, if successful * @return true, if successful
*/ */
public static boolean applyCantTargetAbility(final StaticAbility staticAbility, final Card card, public static boolean applyCantTargetAbility(final StaticAbility st, final Card card,
final SpellAbility spellAbility) { final SpellAbility spellAbility) {
final Map<String, String> params = staticAbility.getMapParams(); final Card hostCard = st.getHostCard();
final Card hostCard = staticAbility.getHostCard();
final Card source = spellAbility.getHostCard(); final Card source = spellAbility.getHostCard();
final Player activator = spellAbility.getActivatingPlayer(); final Player activator = spellAbility.getActivatingPlayer();
if (params.containsKey("AffectedZone")) { if (st.hasParam("ValidPlayer")) {
return false;
}
if (st.hasParam("AffectedZone")) {
boolean inZone = false; boolean inZone = false;
for (final ZoneType zt : ZoneType.listValueOf(params.get("AffectedZone"))) { for (final ZoneType zt : ZoneType.listValueOf(st.getParam("AffectedZone"))) {
if (card.isInZone(zt)) { if (card.isInZone(zt)) {
inZone = true; inZone = true;
break; break;
@@ -65,38 +66,14 @@ public class StaticAbilityCantTarget {
} }
} }
if (params.containsKey("ValidSA")
&& !spellAbility.isValid(params.get("ValidSA").split(","), hostCard.getController(), hostCard, spellAbility)) { if (st.hasParam("ValidCard")
&& !card.isValid(st.getParam("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
return false; return false;
} }
if (params.containsKey("ValidCard")
&& !card.isValid(params.get("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
return false;
}
if (params.containsKey("ValidSource") if (st.hasParam("Hexproof") && (activator != null)) {
&& !source.isValid(params.get("ValidSource").split(","), hostCard.getController(), hostCard, null)) {
return false;
}
if (params.containsKey("Activator") && (activator != null)
&& !activator.isValid(params.get("Activator"), hostCard.getController(), hostCard, spellAbility)) {
return false;
}
if (spellAbility.getParam("ValidTgts")!=null &&
(params.containsKey("SourceCanOnlyTarget")
&& (!spellAbility.getParam("ValidTgts").contains(params.get("SourceCanOnlyTarget"))
|| spellAbility.getParam("ValidTgts").contains(","))
|| spellAbility.getParam("ValidTgts").contains("non" + params.get("SourceCanOnlyTarget")
)
)
){
return false;
}
if (params.containsKey("Hexproof") && (activator != null)) {
for (String k : activator.getKeywords()) { for (String k : activator.getKeywords()) {
if (k.startsWith("IgnoreHexproof")) { if (k.startsWith("IgnoreHexproof")) {
String[] m = k.split(":"); String[] m = k.split(":");
@@ -106,8 +83,81 @@ public class StaticAbilityCantTarget {
} }
} }
} }
if (st.hasParam("Shroud") && (activator != null)) {
for (String k : activator.getKeywords()) {
if (k.startsWith("IgnoreShroud")) {
String[] m = k.split(":");
if (card.isValid(m[1].split(","), activator, source, spellAbility)) {
return false;
}
}
}
}
return common(st, spellAbility);
}
public static boolean applyCantTargetAbility(final StaticAbility st, final Player player,
final SpellAbility spellAbility) {
final Card hostCard = st.getHostCard();
final Card source = spellAbility.getHostCard();
final Player activator = spellAbility.getActivatingPlayer();
if (st.hasParam("ValidCard") || st.hasParam("AffectedZone")) {
return false;
}
if (st.hasParam("ValidPlayer")
&& !player.isValid(st.getParam("ValidPlayer").split(","), hostCard.getController(), hostCard, null)) {
return false;
}
if (st.hasParam("Hexproof") && (activator != null)) {
for (String k : activator.getKeywords()) {
if (k.startsWith("IgnoreHexproof")) {
String[] m = k.split(":");
if (player.isValid(m[1].split(","), activator, source, spellAbility)) {
return false;
}
}
}
}
return true; return true;
} }
protected static boolean common(final StaticAbility st, final SpellAbility spellAbility) {
final Card hostCard = st.getHostCard();
final Card source = spellAbility.getHostCard();
final Player activator = spellAbility.getActivatingPlayer();
if (st.hasParam("ValidSA")
&& !spellAbility.isValid(st.getParam("ValidSA").split(","), hostCard.getController(), hostCard, spellAbility)) {
return false;
}
if (st.hasParam("ValidSource")
&& !source.isValid(st.getParam("ValidSource").split(","), hostCard.getController(), hostCard, null)) {
return false;
}
if (st.hasParam("Activator") && (activator != null)
&& !activator.isValid(st.getParam("Activator"), hostCard.getController(), hostCard, spellAbility)) {
return false;
}
if (spellAbility.hasParam("ValidTgts") &&
(st.hasParam("SourceCanOnlyTarget")
&& (!spellAbility.getParam("ValidTgts").contains(st.getParam("SourceCanOnlyTarget"))
|| spellAbility.getParam("ValidTgts").contains(","))
|| spellAbility.getParam("ValidTgts").contains("non" + st.getParam("SourceCanOnlyTarget")
)
)
){
return false;
}
return true;
}
} }

View File

@@ -3,7 +3,7 @@ ManaCost:4 G G
Types:Legendary Creature Avatar Types:Legendary Creature Avatar
PT:4/4 PT:4/4
K:Shroud K:Shroud
A:AB$ Pump | Cost$ G | ValidTgts$ Player | Defined$ Targeted | TgtPrompt$ Select target player to be able to target Autumn Willow | KW$ Can target CardUIDSource with spells and abilities as though it didn't have shroud. | DefinedKW$ CardUIDSource | StackDescription$ Until end of turn, {p:Targeted} can target CARDNAME as though it didn't have shroud. | SpellDescription$ Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud. A:AB$ Pump | Cost$ G | ValidTgts$ Player | Defined$ Targeted | TgtPrompt$ Select target player to be able to target Autumn Willow | KW$ IgnoreShroud:Card.CardUIDSource | DefinedKW$ CardUIDSource | UntilHostLeavesPlayOrEOT$ True | StackDescription$ Until end of turn, {p:Targeted} can target CARDNAME as though it didn't have shroud. | SpellDescription$ Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/autumn_willow.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/autumn_willow.jpg
Oracle:Shroud (This creature can't be the target of spells or abilities.)\n{G}: Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud. Oracle:Shroud (This creature can't be the target of spells or abilities.)\n{G}: Until end of turn, Autumn Willow can be the target of spells and abilities controlled by target player as though it didn't have shroud.

View File

@@ -4,6 +4,6 @@ Types:Sorcery
A:SP$ Effect | Cost$ 1 W | Name$ Peace Talks Effect | StaticAbilities$ STCantAttack,STCantTarget,STCantTargetPlayer | Duration$ ThisTurnAndNextTurn | SpellDescription$ This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities. A:SP$ Effect | Cost$ 1 W | Name$ Peace Talks Effect | StaticAbilities$ STCantAttack,STCantTarget,STCantTargetPlayer | Duration$ ThisTurnAndNextTurn | SpellDescription$ This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.
SVar:STCantAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature | Description$ Creatures can't attack. SVar:STCantAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature | Description$ Creatures can't attack.
SVar:STCantTarget:Mode$ CantTarget | ValidCard$ Permanent | EffectZone$ Command | ValidSA$ Spell,Activated | Description$ Permanents can't be the targets of spells or activated abilities. SVar:STCantTarget:Mode$ CantTarget | ValidCard$ Permanent | EffectZone$ Command | ValidSA$ Spell,Activated | Description$ Permanents can't be the targets of spells or activated abilities.
SVar:STCantTargetPlayer:Mode$ Continuous | Affected$ Player | EffectZone$ Command | AddKeyword$ You can't be the targets of spells or activated abilities | Description$ Players can't be the targets of spells or activated abilities. SVar:STCantTargetPlayer:Mode$ CantTarget | ValidPlayer$ Player | EffectZone$ Command | ValidSA$ Spell,Activated | Description$Players can't be the targets of spells or activated abilities.
SVar:Picture:http://www.wizards.com/global/images/magic/general/peace_talks.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/peace_talks.jpg
Oracle:This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities. Oracle:This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities.

View File

@@ -3,6 +3,7 @@ ManaCost:1 W U
Types:Enchantment Aura Types:Enchantment Aura
K:Enchant creature K:Enchant creature
A:SP$ Attach | Cost$ 1 W U | ValidTgts$ Creature | AILogic$ Pump A:SP$ Attach | Cost$ 1 W U | ValidTgts$ Creature | AILogic$ Pump
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | AddHiddenKeyword$ CARDNAME can't be the target of spells. | Description$ Enchanted creature gets +0/+2 and can't be the target of spells. S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddToughness$ 2 | Description$ Enchanted creature gets +0/+2.
S:Mode$ CantTarget | ValidCard$ Creature.EnchantedBy | ValidSA$ Spell | Description$ Enchanted creature can't be the target of spells.
SVar:Picture:http://www.wizards.com/global/images/magic/general/spectral_shield.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/spectral_shield.jpg
Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells. Oracle:Enchant creature\nEnchanted creature gets +0/+2 and can't be the target of spells.

View File

@@ -1,8 +1,8 @@
Name:Veil of Summer Name:Veil of Summer
ManaCost:G ManaCost:G
Types:Instant Types:Instant
A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBEffect | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.) A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 |References$ X | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.)
SVar:DBEffect:DB$ Effect | Name$ CARDNAME Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump SVar:DBEffect:DB$ Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump
SVar:AntiMagic:Mode$ Continuous | Affected$ Card.YouCtrl | AffectedZone$ Stack | EffectZone$ Command | AddHiddenKeyword$ CARDNAME can't be countered. | Description$ Spells you control can't be countered this turn. SVar:AntiMagic:Mode$ Continuous | Affected$ Card.YouCtrl | AffectedZone$ Stack | EffectZone$ Command | AddHiddenKeyword$ CARDNAME can't be countered. | Description$ Spells you control can't be countered this turn.
SVar:DBPump:DB$ Pump | Defined$ You | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue | SubAbility$ DBPumpAll SVar:DBPump:DB$ Pump | Defined$ You | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue | SubAbility$ DBPumpAll
SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Permanent.YouCtrl | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Permanent.YouCtrl | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue