- Converted "You may discard CARDNAME any time you could cast an instant." to script

This commit is contained in:
swordshine
2013-12-21 01:08:36 +00:00
parent 05e1161632
commit e13865a966
9 changed files with 88 additions and 53 deletions

1
.gitattributes vendored
View File

@@ -14856,6 +14856,7 @@ forge-gui/src/main/java/forge/game/ability/ApiType.java -text
forge-gui/src/main/java/forge/game/ability/SaTargetRoutines.java -text forge-gui/src/main/java/forge/game/ability/SaTargetRoutines.java -text
forge-gui/src/main/java/forge/game/ability/SpellAbilityEffect.java -text forge-gui/src/main/java/forge/game/ability/SpellAbilityEffect.java -text
forge-gui/src/main/java/forge/game/ability/SpellApiBased.java -text forge-gui/src/main/java/forge/game/ability/SpellApiBased.java -text
forge-gui/src/main/java/forge/game/ability/StaticAbilityApiBased.java -text
forge-gui/src/main/java/forge/game/ability/effects/AbandonEffect.java -text forge-gui/src/main/java/forge/game/ability/effects/AbandonEffect.java -text
forge-gui/src/main/java/forge/game/ability/effects/AddPhaseEffect.java -text forge-gui/src/main/java/forge/game/ability/effects/AddPhaseEffect.java -text
forge-gui/src/main/java/forge/game/ability/effects/AddTurnEffect.java -text forge-gui/src/main/java/forge/game/ability/effects/AddTurnEffect.java -text

View File

@@ -3,7 +3,7 @@ ManaCost:B
Types:Creature Bird Types:Creature Bird
PT:3/2 PT:3/2
K:Flying K:Flying
K:MayDiscardFromHand A:ST$ Discard | Cost$ 0 | Mode$ Defined | DefinedCards$ Self | Optional$ True | DiscardMessage$ Do you want discard this card? | ActivationZone$ Hand | InstantSpeed$ True | SpellDescription$ You may discard CARDNAME any time you could cast an instant.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigSacUnless | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, sacrifice CARDNAME unless you exile the top creature card of your graveyard. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigSacUnless | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, sacrifice CARDNAME unless you exile the top creature card of your graveyard.
SVar:TrigSacUnless:AB$ Sacrifice | Cost$ 0 | Defined$ Self | UnlessCost$ ExileFromGrave<1/Card.TopGraveyardCreature> | UnlessPayer$ You SVar:TrigSacUnless:AB$ Sacrifice | Cost$ 0 | Defined$ Self | UnlessCost$ ExileFromGrave<1/Card.TopGraveyardCreature> | UnlessPayer$ You
SVar:RemAIDeck:True SVar:RemAIDeck:True

View File

@@ -45,6 +45,7 @@ public final class AbilityFactory {
public enum AbilityRecordType { public enum AbilityRecordType {
Ability("AB"), Ability("AB"),
Spell("SP"), Spell("SP"),
StaticAbility("ST"),
SubAbility("DB"); SubAbility("DB");
private final String prefix; private final String prefix;
@@ -59,6 +60,7 @@ public final class AbilityFactory {
switch(this) { switch(this) {
case Ability: return new AbilityApiBased(api, hostCard, abCost, abTgt, mapParams); case Ability: return new AbilityApiBased(api, hostCard, abCost, abTgt, mapParams);
case Spell: return new SpellApiBased(api, hostCard, abCost, abTgt, mapParams); case Spell: return new SpellApiBased(api, hostCard, abCost, abTgt, mapParams);
case StaticAbility: return new StaticAbilityApiBased(api, hostCard, abCost, abTgt, mapParams);
case SubAbility: return new AbilitySub(api, hostCard, abTgt, mapParams); case SubAbility: return new AbilitySub(api, hostCard, abTgt, mapParams);
} }
return null; // exception here would be fine! return null; // exception here would be fine!
@@ -73,6 +75,8 @@ public final class AbilityFactory {
return AbilityRecordType.Ability; return AbilityRecordType.Ability;
} else if (abParams.containsKey(AbilityRecordType.Spell.getPrefix())) { } else if (abParams.containsKey(AbilityRecordType.Spell.getPrefix())) {
return AbilityRecordType.Spell; return AbilityRecordType.Spell;
} else if (abParams.containsKey(AbilityRecordType.StaticAbility.getPrefix())) {
return AbilityRecordType.StaticAbility;
} else if (abParams.containsKey(AbilityRecordType.SubAbility.getPrefix())) { } else if (abParams.containsKey(AbilityRecordType.SubAbility.getPrefix())) {
return AbilityRecordType.SubAbility; return AbilityRecordType.SubAbility;
} else { } else {

View File

@@ -0,0 +1,54 @@
package forge.game.ability;
import java.util.Map;
import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.TargetRestrictions;
public class StaticAbilityApiBased extends AbilityStatic {
private final SpellAbilityEffect effect;
private final SpellAbilityAi ai;
public StaticAbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map<String, String> params0) {
super(sourceCard, abCost, tgt);
params = params0;
api = api0;
effect = api.getSpellEffect();
ai = api.getAi();
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) {
AbilityFactory.adjustChangeZoneTarget(params, this);
}
}
@Override
public String getStackDescription() {
return effect.getStackDescriptionWithSubs(params, this);
}
/* (non-Javadoc)
* @see forge.card.spellability.SpellAbility#resolve()
*/
@Override
public void resolve() {
effect.resolve(this);
}
@Override
public boolean canPlayAI(Player aiPlayer) {
return ai.canPlayAIWithSubs(aiPlayer, this);
}
@Override
public boolean doTrigger(final boolean mandatory, Player aiPlayer) {
return ai.doTriggerAI(aiPlayer, this, mandatory);
}
}

View File

@@ -102,17 +102,22 @@ public class DiscardEffect extends SpellAbilityEffect {
} }
final int numCardsInHand = p.getCardsIn(ZoneType.Hand).size(); final int numCardsInHand = p.getCardsIn(ZoneType.Hand).size();
if (mode.equals("Defined")) { if (mode.equals("Defined")) {
boolean runDiscard = !sa.hasParam("Optional") || p.getController().confirmAction(sa, PlayerActionConfirmMode.Random, sa.getParam("DiscardMessage"));
if (runDiscard) {
final List<Card> toDiscard = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa); final List<Card> toDiscard = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa);
for (final Card c : toDiscard) { for (final Card c : toDiscard) {
boolean hasDiscarded = p.discard(c, sa); boolean hasDiscarded = p.discard(c, sa);
if(hasDiscarded) if (hasDiscarded) {
discarded.add(c); discarded.add(c);
} }
}
if (sa.hasParam("RememberDiscarded")) { if (sa.hasParam("RememberDiscarded")) {
for (final Card c : discarded) { for (final Card c : discarded) {
source.addRemembered(c); source.addRemembered(c);
} }
} }
}
continue; continue;
} }

View File

@@ -1959,8 +1959,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|| keyword.startsWith("PreventAllDamageBy") || keyword.startsWith("PreventAllDamageBy")
|| keyword.startsWith("CantBlock") || keyword.startsWith("CantBlock")
|| keyword.startsWith("CantEquip") || keyword.startsWith("CantEquip")
|| keyword.startsWith("SpellCantTarget") || keyword.startsWith("SpellCantTarget")) {
|| keyword.equals("MayDiscardFromHand")) {
continue; continue;
} }
if (keyword.startsWith("etbCounter")) { if (keyword.startsWith("etbCounter")) {

View File

@@ -3193,10 +3193,6 @@ public class CardFactoryUtil {
} }
} // Morph } // Morph
if (hasKeyword(card, "MayDiscardFromHand") != -1) {
card.addSpellAbility(abilityDiscardSource(card)); // Circling Vultures
}
if (hasKeyword(card, "Unearth") != -1) { if (hasKeyword(card, "Unearth") != -1) {
final int n = hasKeyword(card, "Unearth"); final int n = hasKeyword(card, "Unearth");
if (n != -1) { if (n != -1) {
@@ -3451,42 +3447,6 @@ public class CardFactoryUtil {
} // Ripple } // Ripple
} }
/**
* TODO: Write javadoc for this method.
* @param card
* @return
*/
public static AbilityStatic abilityDiscardSource(final Card sourceCard) {
final AbilityStatic discard = new AbilityStatic(sourceCard, new Cost("0", true), null) {
@Override
public void resolve() {
if (this.getActivatingPlayer().getController().confirmAction(this, null, "Discard this card?")) {
this.getActivatingPlayer().discard(sourceCard, this);
}
}
@Override
public boolean canPlay() {
return sourceCard.isInZone(ZoneType.Hand)
&& sourceCard.getController().equals(this.getActivatingPlayer());
}
@Override
public boolean canPlayAI(Player aiPlayer) {
return false;
}
};
final StringBuilder sb = new StringBuilder();
sb.append("You may discard ").append(sourceCard.getName());
sb.append(" any time you could cast an instant.");
discard.setDescription(sb.toString());
final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - discard this card.");
discard.setStackDescription(sbStack.toString());
return discard;
}
public final static void refreshTotemArmor(Card c) { public final static void refreshTotemArmor(Card c) {
boolean hasKw = c.hasKeyword("Totem armor"); boolean hasKw = c.hasKeyword("Totem armor");

View File

@@ -20,6 +20,7 @@ package forge.game.spellability;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.player.Player;
/** /**
* <p> * <p>
@@ -50,4 +51,15 @@ public abstract class AbilityStatic extends Ability {
this.setTargetRestrictions(tgt); this.setTargetRestrictions(tgt);
} }
} }
@Override
public boolean canPlay() {
Player player = getActivatingPlayer();
if (player == null) {
player = this.getSourceCard().getController();
}
final Card c = this.getSourceCard();
return this.getRestrictions().canPlay(c, this);
}
} }

View File

@@ -340,7 +340,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if ((sp instanceof AbilityTriggered) || (sp instanceof AbilityStatic)) { if ((sp instanceof AbilityTriggered) || (sp instanceof AbilityStatic)) {
// TODO: make working triggered ability // TODO: make working triggered ability
sp.resolve(); AbilityUtils.resolve(sp);
} else { } else {
for (OptionalCost s : sp.getOptionalCosts()) { for (OptionalCost s : sp.getOptionalCosts()) {
source.addOptionalCostPaid(s); source.addOptionalCostPaid(s);