mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
Merge branch 'escapeRefactorFlashback' into 'master'
Refactor Escape and AlternativeCost See merge request core-developers/forge!2361
This commit is contained in:
@@ -109,7 +109,7 @@ public class ComputerUtil {
|
||||
if (chooseTargets != null) {
|
||||
chooseTargets.run();
|
||||
}
|
||||
if (sa.hasParam("Bestow")) {
|
||||
if (sa.isBestow()) {
|
||||
sa.getHostCard().animateBestow();
|
||||
}
|
||||
|
||||
|
||||
@@ -1031,7 +1031,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
Card c = null;
|
||||
List<Card> magnetList = null;
|
||||
String stCheck = null;
|
||||
if (attachSource.isAura() || sa.hasParam("Bestow")) {
|
||||
if (attachSource.isAura() || sa.isBestow()) {
|
||||
stCheck = "EnchantedBy";
|
||||
magnetList = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class GameActionUtil {
|
||||
Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
if (sa.isSpell()) {
|
||||
if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
@@ -88,7 +88,7 @@ public final class GameActionUtil {
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (sa.hasParam("Bestow") && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (sa.isBestow() && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public final class GameActionUtil {
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (sa.isAdventure() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
} else if (sa.isAdventure()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
@@ -146,6 +146,7 @@ public final class GameActionUtil {
|
||||
if (lkicheck) {
|
||||
// double freeze tracker, so it doesn't update view
|
||||
game.getTracker().freeze();
|
||||
source.clearChangedCardKeywords(false);
|
||||
CardCollection preList = new CardCollection(source);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
||||
}
|
||||
@@ -207,6 +208,57 @@ public final class GameActionUtil {
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
// need to be done there before static abilities does reset the card
|
||||
if (sa.isBasicSpell()) {
|
||||
for (final KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
|
||||
if (keyword.startsWith("Escape")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost escapeCost = new Cost(k[1], true);
|
||||
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
|
||||
|
||||
newSA.getMapParams().put("PrecostDesc", "Escape—");
|
||||
newSA.getMapParams().put("CostDesc", escapeCost.toString());
|
||||
|
||||
// makes new SpellDescription
|
||||
final StringBuilder desc = new StringBuilder();
|
||||
desc.append(newSA.getCostDescription());
|
||||
desc.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(desc.toString());
|
||||
|
||||
// Stack Description only for Permanent or it might crash
|
||||
if (source.isPermanent()) {
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(sa.getStackDescription()).append(" (Escaped)");
|
||||
newSA.setStackDescription(sbStack.toString());
|
||||
}
|
||||
newSA.setAlternativeCost(AlternativeCost.Escape);
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
alternatives.add(newSA);
|
||||
} else if (keyword.startsWith("Flashback")) {
|
||||
// if source has No Mana cost, and flashback doesn't have own one,
|
||||
// flashback can't work
|
||||
if (keyword.equals("Flashback") && source.getManaCost().isNoCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SpellAbility flashback = sa.copy(activator);
|
||||
flashback.setAlternativeCost(AlternativeCost.Flashback);
|
||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
// there is a flashback cost (and not the cards cost)
|
||||
if (keyword.contains(":")) {
|
||||
final String[] k = keyword.split(":");
|
||||
flashback.setPayCosts(new Cost(k[1], false));
|
||||
}
|
||||
alternatives.add(flashback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
@@ -244,28 +296,6 @@ public final class GameActionUtil {
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
for (final KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
if (sa.isSpell() && keyword.startsWith("Flashback")) {
|
||||
// if source has No Mana cost, and flashback doesn't have own one,
|
||||
// flashback can't work
|
||||
if (keyword.equals("Flashback") && source.getManaCost().isNoCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SpellAbility flashback = sa.copy(activator);
|
||||
flashback.setFlashBackAbility(true);
|
||||
|
||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
// there is a flashback cost (and not the cards cost)
|
||||
if (keyword.contains(":")) {
|
||||
final String[] k = keyword.split(":");
|
||||
flashback.setPayCosts(new Cost(k[1], false));
|
||||
}
|
||||
alternatives.add(flashback);
|
||||
}
|
||||
}
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
|
||||
@@ -382,21 +382,10 @@ public final class AbilityFactory {
|
||||
* @param mapParams
|
||||
*/
|
||||
private static final void initializeParams(final SpellAbility sa, Map<String, String> mapParams) {
|
||||
if (mapParams.containsKey("Flashback")) {
|
||||
sa.setFlashBackAbility(true);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("NonBasicSpell")) {
|
||||
sa.setBasicSpell(false);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("Dash")) {
|
||||
sa.setDash(true);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("Outlast")) {
|
||||
sa.setOutlast(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1649,7 +1649,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
} else {
|
||||
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
|
||||
}
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") || keyword.startsWith("Escape")) {
|
||||
String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]);
|
||||
if (k.length > 1) {
|
||||
@@ -1794,7 +1794,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|| keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize")
|
||||
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|
||||
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|
||||
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap") || keyword.startsWith("Escape")
|
||||
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|
||||
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
|
||||
// keyword parsing takes care of adding a proper description
|
||||
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
|
||||
@@ -2189,7 +2189,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
sbBefore.append(inst.getReminderText());
|
||||
sbBefore.append("\r\n");
|
||||
} else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness")
|
||||
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")) {
|
||||
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")
|
||||
|| keyword.startsWith("Escape")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
|
||||
@@ -3787,6 +3788,17 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
updateKeywordsCache(currentState);
|
||||
}
|
||||
|
||||
public final boolean clearChangedCardKeywords(final boolean updateView) {
|
||||
if (changedCardKeywords.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
changedCardKeywords.clear();
|
||||
if (updateView) {
|
||||
updateKeywords();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hidden keywords will be left out
|
||||
public final Collection<KeywordInterface> getUnhiddenKeywords() {
|
||||
return getUnhiddenKeywords(currentState);
|
||||
|
||||
@@ -3812,7 +3812,7 @@ public class CardFactoryUtil {
|
||||
final String counters = k[1];
|
||||
final Cost awakenCost = new Cost(k[2], false);
|
||||
|
||||
final SpellAbility awakenSpell = card.getFirstSpellAbility().copy();
|
||||
final SpellAbility awakenSpell = card.getFirstSpellAbility().copyWithDefinedCost(awakenCost);
|
||||
|
||||
final String awaken = "DB$ PutCounter | CounterType$ P1P1 | CounterNum$ "+ counters + " | "
|
||||
+ "ValidTgts$ Land.YouCtrl | TgtPrompt$ Select target land you control | Awaken$ True";
|
||||
@@ -3827,8 +3827,7 @@ public class CardFactoryUtil {
|
||||
String desc = "Awaken " + counters + "—" + awakenCost.toSimpleString() +
|
||||
" (" + inst.getReminderText() + ")";
|
||||
awakenSpell.setDescription(desc);
|
||||
awakenSpell.setBasicSpell(false);
|
||||
awakenSpell.setPayCosts(awakenCost);
|
||||
awakenSpell.setAlternativeCost(AlternativeCost.Awaken);
|
||||
awakenSpell.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(awakenSpell);
|
||||
} else if (keyword.startsWith("Bestow")) {
|
||||
@@ -3845,16 +3844,16 @@ public class CardFactoryUtil {
|
||||
sa.setDescription("Bestow " + ManaCostParser.parse(cost) +
|
||||
" (" + inst.getReminderText() + ")");
|
||||
sa.setStackDescription("Bestow - " + card.getName());
|
||||
sa.setBasicSpell(false);
|
||||
sa.setAlternativeCost(AlternativeCost.Bestow);
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Dash")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String dashString = "SP$ PermanentCreature | Cost$ " + k[1] + " | StackDescription$ CARDNAME (Dash)"
|
||||
+ " | Dash$ True | NonBasicSpell$ True"
|
||||
+ " | SpellDescription$ Dash " + ManaCostParser.parse(k[1]) + " (" + inst.getReminderText() + ")";
|
||||
+ " | Dash$ True | SpellDescription$ Dash " + ManaCostParser.parse(k[1]) + " (" + inst.getReminderText() + ")";
|
||||
|
||||
final SpellAbility newSA = AbilityFactory.getAbility(dashString, card);
|
||||
newSA.setAlternativeCost(AlternativeCost.Dash);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Emerge")) {
|
||||
@@ -3862,13 +3861,12 @@ public class CardFactoryUtil {
|
||||
String costStr = kw[1];
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
|
||||
final SpellAbility newSA = sa.copy();
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(new Cost(costStr, false));
|
||||
|
||||
newSA.getRestrictions().setIsPresent("Creature.YouCtrl+CanBeSacrificedBy");
|
||||
newSA.getMapParams().put("Secondary", "True");
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setIsEmerge(true);
|
||||
newSA.setPayCosts(new Cost(costStr, false));
|
||||
newSA.setAlternativeCost(AlternativeCost.Emerge);
|
||||
|
||||
newSA.setDescription(sa.getDescription() + " (Emerge)");
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
@@ -3938,33 +3936,6 @@ public class CardFactoryUtil {
|
||||
final SpellAbility newSA = AbilityFactory.getAbility(abilityStr.toString(), card);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Escape")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost escapeCost = new Cost(k[1], false);
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
|
||||
|
||||
newSA.getMapParams().put("PrecostDesc", "Escape—");
|
||||
newSA.getMapParams().put("CostDesc", ManaCostParser.parse(k[1]));
|
||||
|
||||
// makes new SpellDescription
|
||||
final StringBuilder desc = new StringBuilder();
|
||||
desc.append(newSA.getCostDescription());
|
||||
desc.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(desc.toString());
|
||||
|
||||
// Stack Description only for Permanent or it might crash
|
||||
if (card.isPermanent()) {
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(sa.getStackDescription()).append(" (Escaped)");
|
||||
newSA.setStackDescription(sbStack.toString());
|
||||
}
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setEscape(true);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Eternalize")) {
|
||||
final String[] kw = keyword.split(":");
|
||||
String costStr = kw[1];
|
||||
@@ -4011,8 +3982,7 @@ public class CardFactoryUtil {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(card.getName()).append(" (Evoked)");
|
||||
newSA.setStackDescription(sb.toString());
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setEvoke(true);
|
||||
newSA.setAlternativeCost(AlternativeCost.Evoke);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Fortify")) {
|
||||
@@ -4169,8 +4139,7 @@ public class CardFactoryUtil {
|
||||
abilityStr.append("AB$ PutCounter | Cost$ ");
|
||||
abilityStr.append(manacost);
|
||||
abilityStr.append(" T | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 ");
|
||||
abilityStr.append("| SorcerySpeed$ True | Outlast$ True ");
|
||||
abilityStr.append("| PrecostDesc$ Outlast");
|
||||
abilityStr.append("| SorcerySpeed$ True | PrecostDesc$ Outlast");
|
||||
Cost cost = new Cost(manacost, true);
|
||||
if (!cost.isOnlyManaCost()) { //Something other than a mana cost
|
||||
abilityStr.append("—");
|
||||
@@ -4182,6 +4151,7 @@ public class CardFactoryUtil {
|
||||
|
||||
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
|
||||
sa.setIntrinsic(intrinsic);
|
||||
sa.setAlternativeCost(AlternativeCost.Outlast);
|
||||
inst.addSpellAbility(sa);
|
||||
|
||||
} else if (keyword.startsWith("Prowl")) {
|
||||
@@ -4200,9 +4170,8 @@ public class CardFactoryUtil {
|
||||
sb.append(newSA.getCostDescription());
|
||||
sb.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(sb.toString());
|
||||
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setProwl(true);
|
||||
|
||||
newSA.setAlternativeCost(AlternativeCost.Prowl);
|
||||
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
@@ -4247,8 +4216,7 @@ public class CardFactoryUtil {
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(cost);
|
||||
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setSpectacle(true);
|
||||
newSA.setAlternativeCost(AlternativeCost.Spectacle);
|
||||
|
||||
String desc = "Spectacle " + cost.toSimpleString() + " (" + inst.getReminderText()
|
||||
+ ")";
|
||||
@@ -4262,8 +4230,7 @@ public class CardFactoryUtil {
|
||||
final Cost surgeCost = new Cost(k[1], false);
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(surgeCost);
|
||||
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setSurged(true);
|
||||
newSA.setAlternativeCost(AlternativeCost.Surge);
|
||||
|
||||
String desc = "Surge " + surgeCost.toSimpleString() + " (" + inst.getReminderText()
|
||||
+ ")";
|
||||
@@ -4374,8 +4341,7 @@ public class CardFactoryUtil {
|
||||
sar.setInstantSpeed(true);
|
||||
|
||||
newSA.getMapParams().put("Secondary", "True");
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.setIsOffering(true);
|
||||
newSA.setAlternativeCost(AlternativeCost.Offering);
|
||||
newSA.setPayCosts(sa.getPayCosts());
|
||||
newSA.setDescription(sa.getDescription() + " (" + offeringType + " offering)");
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
@@ -4410,7 +4376,7 @@ public class CardFactoryUtil {
|
||||
sb.append("| SpellDescription$ (").append(inst.getReminderText()).append(")");
|
||||
|
||||
SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
|
||||
sa.setIsCycling(true);
|
||||
sa.setAlternativeCost(AlternativeCost.Cycling);
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
|
||||
@@ -4434,7 +4400,7 @@ public class CardFactoryUtil {
|
||||
sb.append(" | SpellDescription$ (").append(inst.getReminderText()).append(")");
|
||||
|
||||
SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
|
||||
sa.setIsCycling(true);
|
||||
sa.setAlternativeCost(AlternativeCost.Cycling);
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package forge.game.spellability;
|
||||
|
||||
public enum AlternativeCost {
|
||||
Awaken,
|
||||
Bestow,
|
||||
Cycling, // ActivatedAbility
|
||||
Dash,
|
||||
Emerge,
|
||||
Escape,
|
||||
Evoke,
|
||||
Flashback,
|
||||
Offering,
|
||||
Outlast, // ActivatedAbility
|
||||
Prowl,
|
||||
Spectacle,
|
||||
Surge;
|
||||
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (hasParam("Bestow") && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) {
|
||||
if (isBestow() && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) {
|
||||
// Rule 601.3: cast Bestow with Flash
|
||||
// for the check the card does need to be animated
|
||||
// otherwise the StaticAbility will not found them
|
||||
|
||||
@@ -101,20 +101,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
private int sourceTrigger = -1;
|
||||
private List<Object> triggerRemembered = Lists.newArrayList();
|
||||
|
||||
// TODO use enum for the flags
|
||||
private boolean flashBackAbility = false;
|
||||
private AlternativeCost altCost = null;
|
||||
|
||||
private boolean aftermath = false;
|
||||
private boolean cycling = false;
|
||||
private boolean dash = false;
|
||||
private boolean escape = false;
|
||||
private boolean evoke = false;
|
||||
private boolean prowl = false;
|
||||
private boolean surge = false;
|
||||
private boolean spectacle = false;
|
||||
private boolean offering = false;
|
||||
private boolean emerge = false;
|
||||
|
||||
private boolean cumulativeupkeep = false;
|
||||
private boolean outlast = false;
|
||||
private boolean blessing = false;
|
||||
private Integer chapter = null;
|
||||
|
||||
@@ -388,10 +379,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isCycling() {
|
||||
return cycling;
|
||||
}
|
||||
public final void setIsCycling(final boolean b) {
|
||||
cycling = b;
|
||||
return this.isAlternativeCost(AlternativeCost.Cycling);
|
||||
}
|
||||
|
||||
public Card getOriginalHost() {
|
||||
@@ -780,17 +768,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isBasicSpell() {
|
||||
return basicSpell && !isFlashBackAbility() && !isBuyBackAbility();
|
||||
return basicSpell && this.altCost == null && getRootAbility().optionalCosts.isEmpty();
|
||||
}
|
||||
public void setBasicSpell(final boolean basicSpell0) {
|
||||
basicSpell = basicSpell0;
|
||||
}
|
||||
|
||||
public void setFlashBackAbility(final boolean flashBackAbility0) {
|
||||
flashBackAbility = flashBackAbility0;
|
||||
}
|
||||
public boolean isFlashBackAbility() {
|
||||
return flashBackAbility;
|
||||
return this.isAlternativeCost(AlternativeCost.Flashback);
|
||||
}
|
||||
|
||||
public void setBasicLandAbility(final boolean basicLandAbility0) {
|
||||
@@ -815,10 +800,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isOutlast() {
|
||||
return outlast;
|
||||
}
|
||||
public void setOutlast(boolean outlast0) {
|
||||
outlast = outlast0;
|
||||
return isAlternativeCost(AlternativeCost.Outlast);
|
||||
}
|
||||
|
||||
public boolean isBlessing() {
|
||||
@@ -1165,57 +1147,32 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isDash() {
|
||||
return dash;
|
||||
public final boolean isBestow() {
|
||||
return isAlternativeCost(AlternativeCost.Bestow);
|
||||
}
|
||||
public final void setDash(final boolean isDash) {
|
||||
dash = isDash;
|
||||
|
||||
public final boolean isDash() {
|
||||
return isAlternativeCost(AlternativeCost.Dash);
|
||||
}
|
||||
|
||||
public final boolean isEscape() {
|
||||
return escape;
|
||||
}
|
||||
|
||||
public final void setEscape(final boolean isEscape) {
|
||||
escape = isEscape;
|
||||
return isAlternativeCost(AlternativeCost.Escape);
|
||||
}
|
||||
|
||||
public final boolean isEvoke() {
|
||||
return evoke;
|
||||
}
|
||||
|
||||
public final void setEvoke(final boolean isEvoke) {
|
||||
evoke = isEvoke;
|
||||
return isAlternativeCost(AlternativeCost.Evoke);
|
||||
}
|
||||
|
||||
public final boolean isProwl() {
|
||||
return prowl;
|
||||
}
|
||||
|
||||
public final void setProwl(final boolean isProwl) {
|
||||
prowl = isProwl;
|
||||
return isAlternativeCost(AlternativeCost.Prowl);
|
||||
}
|
||||
|
||||
public final boolean isSurged() {
|
||||
if (surge)
|
||||
return true;
|
||||
SpellAbility parent = getParent();
|
||||
if (parent != null) {
|
||||
return parent.isSurged();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void setSurged(final boolean isSurge) {
|
||||
surge = isSurge;
|
||||
return isAlternativeCost(AlternativeCost.Surge);
|
||||
}
|
||||
|
||||
public final boolean isSpectacle() {
|
||||
return spectacle;
|
||||
}
|
||||
|
||||
public final void setSpectacle(final boolean isSpectacle) {
|
||||
spectacle = isSpectacle;
|
||||
return isAlternativeCost(AlternativeCost.Spectacle);
|
||||
}
|
||||
|
||||
public CardCollection getTappedForConvoke() {
|
||||
@@ -1234,10 +1191,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isEmerge() {
|
||||
return emerge;
|
||||
}
|
||||
public void setIsEmerge(final boolean bEmerge) {
|
||||
emerge = bEmerge;
|
||||
return isAlternativeCost(AlternativeCost.Emerge);
|
||||
}
|
||||
|
||||
public Card getSacrificedAsEmerge() {
|
||||
@@ -1251,10 +1205,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isOffering() {
|
||||
return offering;
|
||||
}
|
||||
public void setIsOffering(final boolean bOffering) {
|
||||
offering = bOffering;
|
||||
return isAlternativeCost(AlternativeCost.Offering);
|
||||
}
|
||||
|
||||
public Card getSacrificedAsOffering() { //for Patron offering
|
||||
@@ -1983,4 +1934,32 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public void setGrantorStatic(final StaticAbility st) {
|
||||
grantorStatic = st;
|
||||
}
|
||||
|
||||
public boolean isAlternativeCost(AlternativeCost ac) {
|
||||
if (ac.equals(altCost)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SpellAbility parent = getParent();
|
||||
if (parent != null) {
|
||||
return parent.isAlternativeCost(ac);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AlternativeCost getAlternativeCost() {
|
||||
if (altCost != null) {
|
||||
return altCost;
|
||||
}
|
||||
|
||||
SpellAbility parent = getParent();
|
||||
if (parent != null) {
|
||||
return parent.getAlternativeCost();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setAlternativeCost(AlternativeCost ac) {
|
||||
altCost = ac;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +97,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
this.setZone(ZoneType.smartValueOf(params.get("ActivationZone")));
|
||||
}
|
||||
|
||||
if (params.containsKey("Flashback")) {
|
||||
this.setZone(ZoneType.Graveyard);
|
||||
}
|
||||
|
||||
if (params.containsKey("SorcerySpeed")) {
|
||||
this.setSorcerySpeed(true);
|
||||
}
|
||||
@@ -202,7 +198,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
Card cp = c;
|
||||
|
||||
// for Bestow need to check the animated State
|
||||
if (sa.isSpell() && sa.hasParam("Bestow")) {
|
||||
if (sa.isSpell() && sa.isBestow()) {
|
||||
// already bestowed or in battlefield, no need to check for spell
|
||||
if (c.isInZone(ZoneType.Battlefield)) {
|
||||
return false;
|
||||
|
||||
@@ -341,11 +341,6 @@ public class WrappedAbility extends Ability {
|
||||
sa.setDescription(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashBackAbility(final boolean flashBackAbility) {
|
||||
sa.setFlashBackAbility(flashBackAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMultiKickerManaCost(final ManaCost cost) {
|
||||
sa.setMultiKickerManaCost(cost);
|
||||
@@ -546,4 +541,16 @@ public class WrappedAbility extends Ability {
|
||||
}
|
||||
// TODO: CardCollection
|
||||
}
|
||||
|
||||
public boolean isAlternativeCost(AlternativeCost ac) {
|
||||
return sa.isAlternativeCost(ac);
|
||||
}
|
||||
|
||||
public AlternativeCost getAlternativeCost() {
|
||||
return sa.getAlternativeCost();
|
||||
}
|
||||
|
||||
public void setAlternativeCost(AlternativeCost ac) {
|
||||
sa.setAlternativeCost(ac);
|
||||
}
|
||||
}
|
||||
@@ -460,7 +460,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
}
|
||||
|
||||
if (thisHasFizzled) { // Fizzle
|
||||
if (sa.hasParam("Bestow")) {
|
||||
if (sa.isBestow()) {
|
||||
// 702.102d: if its target is illegal,
|
||||
// the effect making it an Aura spell ends.
|
||||
// It continues resolving as a creature spell.
|
||||
|
||||
@@ -6,6 +6,6 @@ K:Reach
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerDescription$ When CARDNAME enters the battlefield, it deals damage equal to its power to target creature with flying an opponent controls.
|
||||
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl+withFlying | TgtPrompt$ Select target creature with flying an opponent controls | NumDmg$ X | References$ X
|
||||
SVar:X:Count$CardPower
|
||||
K:Escape:3 G G ExileFromGrave<4/Card.Other>
|
||||
K:Escape:3 G G ExileFromGrave<4/Card.Other/other>
|
||||
K:etbCounter:P1P1:3:ValidCard$ Card.Self+escaped:CARDNAME escapes with three +1/+1 counters on it.
|
||||
Oracle:Reach\nWhen Chainweb Aracnir enters the battlefield, it deals damage equal to its power to target creature with flying an opponent controls.\nEscape — {2}{G}{G}, Exile four other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).\nChainweb Aracnir escapes with three +1/+1 counters on it.
|
||||
|
||||
@@ -5,5 +5,5 @@ Loyalty:5
|
||||
A:AB$ Pump | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +2 | NumDef$ +1 | SpellDescription$ Up to two target creatures you control each get +2/+1 until end of turn.
|
||||
A:AB$ Token | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | TokenAmount$ 2 | TokenScript$ w_1_1_human_soldier | TokenOwner$ You | LegacyImage$ w 1 1 human soldier the | SpellDescription$ Create two 1/1 white Human Soldier creature tokens.
|
||||
A:AB$ GainLife | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | Ultimate$ True | LifeAmount$ 5 | SpellDescription$ You gain 5 life.
|
||||
K:Escape:4 W W ExileFromGrave<4/Card.Other>
|
||||
K:Escape:4 W W ExileFromGrave<4/Card.Other/other>
|
||||
Oracle:−1: Up to two target creatures you control each get +2/+1 until end of turn.\n−2: Create two 1/1 white Human Soldier creature tokens.\n−3: You gain 5 life.\nEscape—{4}{W}{W}, Exile four other cards from your graveyard. (You may cast this card from your graveyard for its escape cost.)
|
||||
|
||||
@@ -2,5 +2,5 @@ Name:Fruit of Tizerus
|
||||
ManaCost:B
|
||||
Types:Sorcery
|
||||
A:SP$ LoseLife | Cost$ B | ValidTgts$ Player | TgtPrompt$ Select a player | LifeAmount$ 2 | SpellDescription$ Target player loses 2 life.
|
||||
K:Escape:3 B ExileFromGrave<3/Card.Other>
|
||||
K:Escape:3 B ExileFromGrave<3/Card.Other/other>
|
||||
Oracle:Target player loses 2 life.\nEscape — {3}{B}, Exile three other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).
|
||||
|
||||
@@ -2,5 +2,5 @@ Name:Satyr's Cunning
|
||||
ManaCost:R
|
||||
Types:Sorcery
|
||||
A:SP$ Token | Cost$ R | TokenAmount$ 1 | TokenScript$ r_1_1_satyr_noblock | TokenOwner$ You | LegacyImage$ r 1 1 satyr noblock thb | SpellDescription$ Create a 1/1 red Satyr creature token with "This creature can't block."
|
||||
K:Escape:2 R ExileFromGrave<2/Card.Other>
|
||||
Oracle:Create a 1/1 red Satyr creature token with "This creature can't block."\nEscape — {2}{R}, Exile two other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).
|
||||
K:Escape:2 R ExileFromGrave<2/Card.Other/other>
|
||||
Oracle:Create a 1/1 red Satyr creature token with "This creature can't block."\nEscape — {2}{R}, Exile two other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).
|
||||
|
||||
11
forge-gui/res/cardsfolder/upcoming/underworld_breach.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/underworld_breach.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Underworld Breach
|
||||
ManaCost:1 R
|
||||
Types:Enchantment
|
||||
S:Mode$ Continuous | Affected$ Card.YouOwn+nonLand | AffectedZone$ Graveyard | AddKeyword$ Escape:CardManaCost ExileFromGrave<3/Card.Other/other> | Description$ Each nonland card in your graveyard has escape. The escape cost is equal to the card's mana cost plus exile three other cards from your graveyard.
|
||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of the end step, sacrifice CARDNAME.
|
||||
SVar:TrigSac:DB$ Sacrifice | SacValid$ Self
|
||||
SVar:EndOfTurnLeavePlay:True
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Each nonland card in your graveyard has escape. The escape cost is equal to the card's mana cost plus exile three other cards from your graveyard.\nAt the beginning of the end step, sacrifice Underworld Breach.
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ ManaCost:1 R
|
||||
Types:Creature Elemental Hound
|
||||
PT:3/1
|
||||
K:CARDNAME attacks each combat if able.
|
||||
K:Escape:3 R ExileFromGrave<3/Card.Other>
|
||||
K:Escape:3 R ExileFromGrave<3/Card.Other/other>
|
||||
K:etbCounter:P1P1:1:ValidCard$ Card.Self+escaped:CARDNAME escapes with a +1/+1 counter on it.
|
||||
Oracle:Underworld Rage-Hound attacks each combat if able.\nEscape — {3}{R}, Exile three other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).\nUnderworld Rage-Hound escapes with a +1/+1 counter on it.
|
||||
|
||||
@@ -2,6 +2,6 @@ Name:Voracious Typhon
|
||||
ManaCost:2 G G
|
||||
Types:Creature Snake Beast
|
||||
PT:4/4
|
||||
K:Escape:5 G G ExileFromGrave<4/Card.Other>
|
||||
K:Escape:5 G G ExileFromGrave<4/Card.Other/other>
|
||||
K:etbCounter:P1P1:3:ValidCard$ Card.Self+escaped:CARDNAME escapes with three +1/+1 counters on it.
|
||||
Oracle:Escape — {5}{G}{G}, Exile four other cards from your graveyard. (You may cast this card from your graveyard for its escape cost).\nVoracious Typhon escapes with three +1/+1 counters on it.
|
||||
|
||||
@@ -87,7 +87,7 @@ public class HumanPlay {
|
||||
|
||||
sa = AbilityUtils.addSpliceEffects(sa);
|
||||
|
||||
if (sa.hasParam("Bestow")) {
|
||||
if (sa.isBestow()) {
|
||||
source.animateBestow();
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (!prerequisitesMet) {
|
||||
if (!ability.isTrigger()) {
|
||||
rollbackAbility(fromZone, fromState, zonePosition, payment);
|
||||
rollbackAbility(fromZone, zonePosition, payment);
|
||||
if (ability.getHostCard().isMadness()) {
|
||||
// if a player failed to play madness cost, move the card to graveyard
|
||||
Card newCard = game.getAction().moveToGraveyard(c, null);
|
||||
@@ -244,14 +244,13 @@ public class HumanPlaySpellAbility {
|
||||
}
|
||||
}
|
||||
|
||||
private void rollbackAbility(final Zone fromZone, final CardStateName fromState, final int zonePosition, CostPayment payment) {
|
||||
private void rollbackAbility(final Zone fromZone, final int zonePosition, CostPayment payment) {
|
||||
// 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, null);
|
||||
ability.getHostCard().setState(fromState, true);
|
||||
}
|
||||
|
||||
clearTargets(ability);
|
||||
|
||||
Reference in New Issue
Block a user