- 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/SpellAbilityEffect.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/AddPhaseEffect.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
PT:3/2
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.
SVar:TrigSacUnless:AB$ Sacrifice | Cost$ 0 | Defined$ Self | UnlessCost$ ExileFromGrave<1/Card.TopGraveyardCreature> | UnlessPayer$ You
SVar:RemAIDeck:True

View File

@@ -45,6 +45,7 @@ public final class AbilityFactory {
public enum AbilityRecordType {
Ability("AB"),
Spell("SP"),
StaticAbility("ST"),
SubAbility("DB");
private final String prefix;
@@ -59,6 +60,7 @@ public final class AbilityFactory {
switch(this) {
case Ability: return new AbilityApiBased(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);
}
return null; // exception here would be fine!
@@ -73,6 +75,8 @@ public final class AbilityFactory {
return AbilityRecordType.Ability;
} else if (abParams.containsKey(AbilityRecordType.Spell.getPrefix())) {
return AbilityRecordType.Spell;
} else if (abParams.containsKey(AbilityRecordType.StaticAbility.getPrefix())) {
return AbilityRecordType.StaticAbility;
} else if (abParams.containsKey(AbilityRecordType.SubAbility.getPrefix())) {
return AbilityRecordType.SubAbility;
} 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,15 +102,20 @@ public class DiscardEffect extends SpellAbilityEffect {
}
final int numCardsInHand = p.getCardsIn(ZoneType.Hand).size();
if (mode.equals("Defined")) {
final List<Card> toDiscard = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa);
for (final Card c : toDiscard) {
boolean hasDiscarded = p.discard(c, sa);
if(hasDiscarded)
discarded.add(c);
}
if (sa.hasParam("RememberDiscarded")) {
for (final Card c : discarded) {
source.addRemembered(c);
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);
for (final Card c : toDiscard) {
boolean hasDiscarded = p.discard(c, sa);
if (hasDiscarded) {
discarded.add(c);
}
}
if (sa.hasParam("RememberDiscarded")) {
for (final Card c : discarded) {
source.addRemembered(c);
}
}
}
continue;

View File

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

View File

@@ -3193,10 +3193,6 @@ public class CardFactoryUtil {
}
} // Morph
if (hasKeyword(card, "MayDiscardFromHand") != -1) {
card.addSpellAbility(abilityDiscardSource(card)); // Circling Vultures
}
if (hasKeyword(card, "Unearth") != -1) {
final int n = hasKeyword(card, "Unearth");
if (n != -1) {
@@ -3451,42 +3447,6 @@ public class CardFactoryUtil {
} // 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) {
boolean hasKw = c.hasKeyword("Totem armor");

View File

@@ -20,6 +20,7 @@ package forge.game.spellability;
import forge.card.mana.ManaCost;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.player.Player;
/**
* <p>
@@ -50,4 +51,15 @@ public abstract class AbilityStatic extends Ability {
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)) {
// TODO: make working triggered ability
sp.resolve();
AbilityUtils.resolve(sp);
} else {
for (OptionalCost s : sp.getOptionalCosts()) {
source.addOptionalCostPaid(s);