diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 5137b155aa0..fa6eb390890 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -27,10 +27,7 @@ import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory.AbilityRecordType; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; -import forge.game.card.Card; -import forge.game.card.CardCollectionView; -import forge.game.card.CardPlayOption; -import forge.game.card.CardPredicates; +import forge.game.card.*; import forge.game.card.CardPlayOption.PayManaCost; import forge.game.cost.Cost; import forge.game.mana.ManaCostBeingPaid; @@ -151,6 +148,20 @@ public final class GameActionUtil { newSA.setBasicSpell(false); newSA.setPayCosts(newSA.getPayCosts().copyWithDefinedMana(o.getAltManaCost())); changedManaCost = true; + if (host.hasSVar("AsForetoldSplitCMCHack")) { + // FIXME: A temporary workaround for As Foretold interaction with split cards, better solution needed. + if (sa.isLeftSplit()) { + int leftCMC = sa.getHostCard().getCMC(Card.SplitCMCMode.LeftSplitCMC); + if (leftCMC > host.getCounters(CounterType.TIME)) { + continue; + } + } else if (sa.isRightSplit()) { + int rightCMC = sa.getHostCard().getCMC(Card.SplitCMCMode.RightSplitCMC); + if (rightCMC > host.getCounters(CounterType.TIME)) { + continue; + } + } + } } if (changedManaCost) { if ("0".equals(sa.getParam("ActivationLimit")) && sa.getHostCard().getManaCost().isNoCost()) { diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index b93d5eb8360..6b438eb9008 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -3,6 +3,7 @@ package forge.game.card; import java.util.Collections; import java.util.List; +import forge.card.CardStateName; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; @@ -95,6 +96,36 @@ public class CardProperty { if (!card.isFlipCard()) { return false; } + } else if (property.equals("Split")) { + if (!card.isSplitCard()) { + return false; + } + } else if (property.equals("NotSplit")) { + if (card.isSplitCard()) { + return false; + } + } else if (property.startsWith("leftcmc") || property.startsWith("rightcmc")) { + int x; + int y = 0; + String rhs = ""; + + if (property.startsWith("leftcmc")) { + rhs = property.substring(9); + y = card.getState(CardStateName.LeftSplit).getManaCost().getCMC(); + } else if (property.startsWith("rightcmc")) { + rhs = property.substring(10); + y = card.getState(CardStateName.RightSplit).getManaCost().getCMC(); + } + + try { + x = Integer.parseInt(rhs); + } catch (final NumberFormatException e) { + x = AbilityUtils.calculateAmount(source, rhs, spellAbility); + } + + if (!Expressions.compare(y, property, x)) { + return false; + } } else if (property.startsWith("YouCtrl")) { if (!controller.equals(sourceController)) { return false; diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java index d00b6179dce..7ebc5b16c9a 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java @@ -252,6 +252,10 @@ public class SpellAbilityRestriction extends SpellAbilityVariables { break; } } + if (cardZone.is(ZoneType.Graveyard) && sa.isAftermath()) { + // Special exclusion for Aftermath, useful for e.g. As Foretold + return true; + } if (!hasOtherGrantor) { return false; } diff --git a/forge-gui/release-files/ISSUES.txt b/forge-gui/release-files/ISSUES.txt index 0b35b96abee..e789467eadc 100644 --- a/forge-gui/release-files/ISSUES.txt +++ b/forge-gui/release-files/ISSUES.txt @@ -1,6 +1,6 @@ Images for the latest sets will be available soon. -As Foretold does not yet interact correctly with split cards as described in the official rulings. You will only be able to cast a split card for {0} via As Foretold if you have the number of time counters on it equal to or greater than the combined CMC of the split card, at which point you will be able to cast either half if it's a regular (non-Aftermath) split card. You will not yet be able to cast the Aftermath half of a split card using As Foretold at all. +The interaction of As Foretold with split cards is implemented in a rather hacky way and could use an improvement. Ideally, MayPlay should be adding the static ability of As Foretold only to the split half that is actually castable, and to both halves if they are each individually castable via As Foretold. However, this is rather tricky to implement. Online play is unfinished and is not fully operational. While several users have reported moderate success in getting a simple online match going, most users have experienced crashes and/or inability to start a server or connect to it. At the moment, we do not have a dedicated developer actively working on the online play feature, so we do not have an ETA as to when this feature will become finished. If you have working knowledge of Java that, you believe, is adequate to help seeing this feature through to completion, please consider offering your help in our Discord channel. diff --git a/forge-gui/res/cardsfolder/a/as_foretold.txt b/forge-gui/res/cardsfolder/a/as_foretold.txt index 36b1c6e1baa..6dbf0a10d88 100644 --- a/forge-gui/res/cardsfolder/a/as_foretold.txt +++ b/forge-gui/res/cardsfolder/a/as_foretold.txt @@ -2,8 +2,10 @@ Name:As Foretold ManaCost:2 U Types:Enchantment T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your upkeep, put a time counter on CARDNAME. -S:Mode$ Continuous | MayPlay$ True | MayPlayAltManaCost$ 0 | MayPlayLimit$ 1 | MayPlayDontGrantZonePermissions$ True | Affected$ Card.YouCtrl+cmcLEX | AffectedZone$ Hand,Graveyard,Library,Exile | Description$ Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast with converted mana cost X or less, where X is the number of time counters on CARDNAME. (Known Issue: this card does not yet interact correctly with split cards.) -SVar:X:Count$CardCounters.TIME SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ TIME | CounterNum$ 1 +S:Mode$ Continuous | MayPlay$ True | MayPlayAltManaCost$ 0 | MayPlayLimit$ 1 | MayPlayDontGrantZonePermissions$ True | Affected$ Card.YouCtrl+NotSplit+nonLand+cmcLEX | AffectedZone$ Hand,Graveyard,Library,Exile | Description$ Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast with converted mana cost X or less, where X is the number of time counters on CARDNAME. +S:Mode$ Continuous | MayPlay$ True | MayPlayAltManaCost$ 0 | MayPlayLimit$ 1 | MayPlayDontGrantZonePermissions$ True | Affected$ Card.YouCtrl+Split+nonLand+leftcmcLEX,Card.YouCtrl+Split+faceUp+rightcmcLEX | AffectedZone$ Hand,Graveyard,Library,Exile | Secondary$ True +SVar:X:Count$CardCounters.TIME +SVar:AsForetoldSplitCMCHack:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/as_foretold.jpg Oracle:At the beginning of your upkeep, put a time counter on As Foretold.\nOnce each turn, you may pay {0} rather than pay the mana cost for a spell you cast with converted mana cost X or less, where X is the number of time counters on As Foretold.