mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Extra cost gift rework (#6733)
* added PromisedGift as xCount * SpellAbilityAi: move chooseOptionalCosts * SpellAbilityAi: chooseOptionalCosts check for invalid targets * Give API logic access to castSA * ~ add BaseSpell for PromiseGift * Unearth: remove unneeded Param * PromiseGift: uses AITgts to stop AI from using Gift when not needed --------- Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
This commit is contained in:
@@ -775,9 +775,15 @@ public class AiController {
|
|||||||
if (currentState != null) {
|
if (currentState != null) {
|
||||||
host.setState(sa.getCardStateName(), false);
|
host.setState(sa.getCardStateName(), false);
|
||||||
}
|
}
|
||||||
|
if (sa.isSpell()) {
|
||||||
|
host.setCastSA(sa);
|
||||||
|
}
|
||||||
|
|
||||||
AiPlayDecision decision = canPlayAndPayForFace(sa);
|
AiPlayDecision decision = canPlayAndPayForFace(sa);
|
||||||
|
|
||||||
|
if (sa.isSpell()) {
|
||||||
|
host.setCastSA(null);
|
||||||
|
}
|
||||||
if (currentState != null) {
|
if (currentState != null) {
|
||||||
host.setState(currentState, false);
|
host.setState(currentState, false);
|
||||||
}
|
}
|
||||||
@@ -918,7 +924,7 @@ public class AiController {
|
|||||||
Sentry.setExtra("Card", card.getName());
|
Sentry.setExtra("Card", card.getName());
|
||||||
Sentry.setExtra("SA", sa.toString());
|
Sentry.setExtra("SA", sa.toString());
|
||||||
|
|
||||||
boolean canPlay = SpellApiToAi.Converter.get(sa.getApi()).canPlayAIWithSubs(player, sa);
|
boolean canPlay = SpellApiToAi.Converter.get(sa).canPlayAIWithSubs(player, sa);
|
||||||
|
|
||||||
// remove added extra
|
// remove added extra
|
||||||
Sentry.removeExtra("Card");
|
Sentry.removeExtra("Card");
|
||||||
@@ -1296,9 +1302,9 @@ public class AiController {
|
|||||||
if (spell instanceof SpellApiBased) {
|
if (spell instanceof SpellApiBased) {
|
||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
if (withoutPayingManaCost) {
|
if (withoutPayingManaCost) {
|
||||||
chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerNoCostWithSubs(player, spell, mandatory);
|
chance = SpellApiToAi.Converter.get(spell).doTriggerNoCostWithSubs(player, spell, mandatory);
|
||||||
} else {
|
} else {
|
||||||
chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
chance = SpellApiToAi.Converter.get(spell).doTriggerAI(player, spell, mandatory);
|
||||||
}
|
}
|
||||||
if (!chance) {
|
if (!chance) {
|
||||||
return AiPlayDecision.TargetingFailed;
|
return AiPlayDecision.TargetingFailed;
|
||||||
@@ -1620,38 +1626,45 @@ public class AiController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getHostCard().hasKeyword(Keyword.STORM)
|
if (sa.getHostCard().hasKeyword(Keyword.STORM)
|
||||||
&& sa.getApi() != ApiType.Counter // AI would suck at trying to deliberately proc a Storm counterspell
|
&& sa.getApi() != ApiType.Counter // AI would suck at trying to deliberately proc a Storm counterspell
|
||||||
&& player.getZone(ZoneType.Hand).contains(
|
&& player.getZone(ZoneType.Hand).contains(
|
||||||
Predicate.not(CardPredicates.LANDS.or(CardPredicates.hasKeyword("Storm")))
|
Predicate.not(CardPredicates.LANDS.or(CardPredicates.hasKeyword("Storm")))
|
||||||
)) {
|
)) {
|
||||||
if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) {
|
if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) {
|
||||||
// skip evaluating Storm unless we reached the minimum Storm count
|
// skip evaluating Storm unless we reached the minimum Storm count
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
// living end AI decks
|
|
||||||
// TODO: generalize the implementation so that superfluous logic-specific checks for life, library size, etc. aren't needed
|
|
||||||
AiPlayDecision aiPlayDecision = AiPlayDecision.CantPlaySa;
|
|
||||||
if (useLivingEnd) {
|
|
||||||
if (sa.isCycling() && sa.canCastTiming(player) && player.getCardsIn(ZoneType.Library).size() >= 10) {
|
|
||||||
if (ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
|
||||||
if (sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPayLife.class)
|
|
||||||
&& !player.cantLoseForZeroOrLessLife()
|
|
||||||
&& player.getLife() <= sa.getPayCosts().getCostPartByType(CostPayLife.class).getAbilityAmount(sa) * 2) {
|
|
||||||
aiPlayDecision = AiPlayDecision.CantAfford;
|
|
||||||
} else {
|
|
||||||
aiPlayDecision = AiPlayDecision.WillPlay;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (sa.getHostCard().hasKeyword(Keyword.CASCADE)) {
|
}
|
||||||
if (isLifeInDanger) { //needs more tune up for certain conditions
|
|
||||||
aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa : AiPlayDecision.WillPlay;
|
// living end AI decks
|
||||||
} else if (CardLists.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.CREATURES).size() > 4) {
|
// TODO: generalize the implementation so that superfluous logic-specific checks for life, library size, etc. aren't needed
|
||||||
|
AiPlayDecision aiPlayDecision = AiPlayDecision.CantPlaySa;
|
||||||
|
if (useLivingEnd) {
|
||||||
|
if (sa.isCycling() && sa.canCastTiming(player)
|
||||||
|
&& player.getCardsIn(ZoneType.Library).size() >= 10) {
|
||||||
|
if (ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) {
|
||||||
|
if (sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPayLife.class)
|
||||||
|
&& !player.cantLoseForZeroOrLessLife() && player.getLife() <= sa.getPayCosts()
|
||||||
|
.getCostPartByType(CostPayLife.class).getAbilityAmount(sa) * 2) {
|
||||||
|
aiPlayDecision = AiPlayDecision.CantAfford;
|
||||||
|
} else {
|
||||||
|
aiPlayDecision = AiPlayDecision.WillPlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sa.getHostCard().hasKeyword(Keyword.CASCADE)) {
|
||||||
|
if (isLifeInDanger) { // needs more tune up for certain conditions
|
||||||
|
aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa
|
||||||
|
: AiPlayDecision.WillPlay;
|
||||||
|
} else if (CardLists
|
||||||
|
.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.CREATURES)
|
||||||
|
.size() > 4) {
|
||||||
if (player.getCreaturesInPlay().size() >= 4) // it's good minimum
|
if (player.getCreaturesInPlay().size() >= 4) // it's good minimum
|
||||||
continue;
|
continue;
|
||||||
else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && ComputerUtilCost.canPayCost(sa, player, sa.isTrigger()))
|
else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player)
|
||||||
aiPlayDecision = AiPlayDecision.WillPlay;// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard
|
&& ComputerUtilCost.canPayCost(sa, player, sa.isTrigger()))
|
||||||
|
aiPlayDecision = AiPlayDecision.WillPlay;
|
||||||
|
// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1726,7 +1739,7 @@ public class AiController {
|
|||||||
if (spell instanceof WrappedAbility)
|
if (spell instanceof WrappedAbility)
|
||||||
return doTrigger(((WrappedAbility) spell).getWrappedAbility(), mandatory);
|
return doTrigger(((WrappedAbility) spell).getWrappedAbility(), mandatory);
|
||||||
if (spell.getApi() != null)
|
if (spell.getApi() != null)
|
||||||
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
return SpellApiToAi.Converter.get(spell).doTriggerAI(player, spell, mandatory);
|
||||||
if (spell.getPayCosts() == Cost.Zero && !spell.usesTargeting()) {
|
if (spell.getPayCosts() == Cost.Zero && !spell.usesTargeting()) {
|
||||||
// For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about
|
// For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -906,7 +906,7 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
// Run non-mandatory trigger.
|
// Run non-mandatory trigger.
|
||||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||||
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
|
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA).doTriggerAI(ai, exSA, false)) {
|
||||||
// AI would not run this trigger if given the chance
|
// AI would not run this trigger if given the chance
|
||||||
return sacrificed;
|
return sacrificed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import forge.game.cost.CostRemoveCounter;
|
|||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.OptionalCost;
|
||||||
import forge.game.spellability.OptionalCostValue;
|
import forge.game.spellability.OptionalCostValue;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
@@ -122,6 +123,10 @@ public class ComputerUtilAbility {
|
|||||||
boolean choseOptCost = false;
|
boolean choseOptCost = false;
|
||||||
List<OptionalCostValue> list = GameActionUtil.getOptionalCostValues(sa);
|
List<OptionalCostValue> list = GameActionUtil.getOptionalCostValues(sa);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
|
// still add base spell in case of Promise Gift
|
||||||
|
if (list.stream().anyMatch(ocv -> ocv.getType().equals(OptionalCost.PromiseGift))) {
|
||||||
|
result.add(sa);
|
||||||
|
}
|
||||||
list = player.getController().chooseOptionalCosts(sa, list);
|
list = player.getController().chooseOptionalCosts(sa, list);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
choseOptCost = true;
|
choseOptCost = true;
|
||||||
|
|||||||
@@ -1491,7 +1491,7 @@ public class ComputerUtilMana {
|
|||||||
AbilitySub sub = m.getSubAbility();
|
AbilitySub sub = m.getSubAbility();
|
||||||
// We really shouldn't be hardcoding names here. ChkDrawback should just return true for them
|
// We really shouldn't be hardcoding names here. ChkDrawback should just return true for them
|
||||||
if (sub != null && !card.getName().equals("Pristine Talisman") && !card.getName().equals("Zhur-Taa Druid")) {
|
if (sub != null && !card.getName().equals("Pristine Talisman") && !card.getName().equals("Zhur-Taa Druid")) {
|
||||||
if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
|
if (!SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
needsLimitedResources = true; // TODO: check for good drawbacks (gainLife)
|
needsLimitedResources = true; // TODO: check for good drawbacks (gainLife)
|
||||||
@@ -1571,7 +1571,7 @@ public class ComputerUtilMana {
|
|||||||
// don't use abilities with dangerous drawbacks
|
// don't use abilities with dangerous drawbacks
|
||||||
AbilitySub sub = m.getSubAbility();
|
AbilitySub sub = m.getSubAbility();
|
||||||
if (sub != null) {
|
if (sub != null) {
|
||||||
if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
|
if (!SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@@ -352,11 +351,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
if (delayedReveal != null) {
|
if (delayedReveal != null) {
|
||||||
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||||
}
|
}
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -398,11 +393,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
||||||
Map<String, Object> params) {
|
Map<String, Object> params) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseSingleSpellAbility(player, sa, spells, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseSingleSpellAbility(player, sa, spells, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -876,11 +867,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
|
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseNumber(player, sa, min, max, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseNumber(player, sa, min, max, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -982,11 +969,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) {
|
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseBinary(kindOfChoice, sa, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseBinary(kindOfChoice, sa, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1056,11 +1039,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
if (options.size() <= 1) {
|
if (options.size() <= 1) {
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseCounterType(options, sa, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseCounterType(options, sa, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1217,7 +1196,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers) {
|
public boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, FCollectionView<Player> allPayers) {
|
||||||
if (SpellApiToAi.Converter.get(sa.getApi()).willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers)) {
|
if (SpellApiToAi.Converter.get(sa).willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers)) {
|
||||||
if (!ComputerUtilCost.canPayCost(cost, sa, player, true)) {
|
if (!ComputerUtilCost.canPayCost(cost, sa, player, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1397,11 +1376,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message) {
|
public String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseCardName(player, sa, faces);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseCardName(player, sa, faces);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1506,11 +1481,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ICardFace chooseSingleCardFace(SpellAbility sa, List<ICardFace> faces, String message) {
|
public ICardFace chooseSingleCardFace(SpellAbility sa, List<ICardFace> faces, String message) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseCardFace(player, sa, faces);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseCardFace(player, sa, faces);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1520,11 +1491,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CardState chooseSingleCardState(SpellAbility sa, List<CardState> states, String message, Map<String, Object> params) {
|
public CardState chooseSingleCardState(SpellAbility sa, List<CardState> states, String message, Map<String, Object> params) {
|
||||||
ApiType api = sa.getApi();
|
return SpellApiToAi.Converter.get(sa).chooseCardState(player, sa, states, params);
|
||||||
if (null == api) {
|
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
|
||||||
}
|
|
||||||
return SpellApiToAi.Converter.get(api).chooseCardState(player, sa, states, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1576,32 +1543,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, List<OptionalCostValue> optionalCostValues) {
|
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, List<OptionalCostValue> optionalCostValues) {
|
||||||
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
|
return SpellApiToAi.Converter.get(chosen).chooseOptionalCosts(chosen, player, optionalCostValues);
|
||||||
Cost costSoFar = chosen.getPayCosts().copy();
|
|
||||||
|
|
||||||
for (OptionalCostValue opt : optionalCostValues) {
|
|
||||||
// Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps)
|
|
||||||
Cost fullCost = opt.getCost().copy().add(costSoFar);
|
|
||||||
SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost);
|
|
||||||
|
|
||||||
// Playability check for Kicker
|
|
||||||
if (opt.getType() == OptionalCost.Kicker1 || opt.getType() == OptionalCost.Kicker2) {
|
|
||||||
SpellAbility kickedSaCopy = fullCostSa.copy();
|
|
||||||
kickedSaCopy.addOptionalCost(opt.getType());
|
|
||||||
Card copy = CardCopyService.getLKICopy(chosen.getHostCard());
|
|
||||||
copy.setCastSA(kickedSaCopy);
|
|
||||||
if (ComputerUtilCard.checkNeedsToPlayReqs(copy, kickedSaCopy) != AiPlayDecision.WillPlay) {
|
|
||||||
continue; // don't choose kickers we don't want to play
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ComputerUtilCost.canPayCost(fullCostSa, player, false)) {
|
|
||||||
chosenOptCosts.add(opt);
|
|
||||||
costSoFar.add(opt.getCost());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chosenOptCosts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1661,5 +1603,4 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
return choices;
|
return choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import forge.card.mana.ManaCost;
|
|||||||
import forge.card.mana.ManaCostParser;
|
import forge.card.mana.ManaCostParser;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCopyService;
|
||||||
import forge.game.card.CardState;
|
import forge.game.card.CardState;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
@@ -23,6 +24,8 @@ import forge.game.player.Player;
|
|||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
|
import forge.game.spellability.OptionalCost;
|
||||||
|
import forge.game.spellability.OptionalCostValue;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityCondition;
|
import forge.game.spellability.SpellAbilityCondition;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
@@ -305,7 +308,7 @@ public abstract class SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
|
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
|
||||||
final AbilitySub subAb = ab.getSubAbility();
|
final AbilitySub subAb = ab.getSubAbility();
|
||||||
return SpellApiToAi.Converter.get(ab.getApi()).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
|
return SpellApiToAi.Converter.get(ab).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
@@ -410,4 +413,33 @@ public abstract class SpellAbilityAi {
|
|||||||
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
||||||
return MyRandom.getRandom().nextBoolean();
|
return MyRandom.getRandom().nextBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, Player player, List<OptionalCostValue> optionalCostValues) {
|
||||||
|
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
|
||||||
|
Cost costSoFar = chosen.getPayCosts().copy();
|
||||||
|
|
||||||
|
for (OptionalCostValue opt : optionalCostValues) {
|
||||||
|
// Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps)
|
||||||
|
Cost fullCost = opt.getCost().copy().add(costSoFar);
|
||||||
|
SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost);
|
||||||
|
|
||||||
|
// Playability check for Kicker
|
||||||
|
if (opt.getType() == OptionalCost.Kicker1 || opt.getType() == OptionalCost.Kicker2) {
|
||||||
|
SpellAbility kickedSaCopy = fullCostSa.copy();
|
||||||
|
kickedSaCopy.addOptionalCost(opt.getType());
|
||||||
|
Card copy = CardCopyService.getLKICopy(chosen.getHostCard());
|
||||||
|
copy.setCastSA(kickedSaCopy);
|
||||||
|
if (ComputerUtilCard.checkNeedsToPlayReqs(copy, kickedSaCopy) != AiPlayDecision.WillPlay) {
|
||||||
|
continue; // don't choose kickers we don't want to play
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ComputerUtilCost.canPayCost(fullCostSa, player, false)) {
|
||||||
|
chosenOptCosts.add(opt);
|
||||||
|
costSoFar.add(opt.getCost());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosenOptCosts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.ai.ability.*;
|
import forge.ai.ability.*;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.ReflectionUtil;
|
import forge.util.ReflectionUtil;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public enum SpellApiToAi {
|
public enum SpellApiToAi {
|
||||||
@@ -207,6 +209,14 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.InternalRadiation, AlwaysPlayAi.class)
|
.put(ApiType.InternalRadiation, AlwaysPlayAi.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
public SpellAbilityAi get(final SpellAbility sa) {
|
||||||
|
ApiType api = sa.getApi();
|
||||||
|
if (null == api) {
|
||||||
|
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
||||||
|
}
|
||||||
|
return get(api);
|
||||||
|
}
|
||||||
|
|
||||||
public SpellAbilityAi get(final ApiType api) {
|
public SpellAbilityAi get(final ApiType api) {
|
||||||
SpellAbilityAi result = apiToInstance.get(api);
|
SpellAbilityAi result = apiToInstance.get(api);
|
||||||
if (null == result) {
|
if (null == result) {
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final AbilitySub subAb = sa.getSubAbility();
|
final AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
|
return subAb == null || SpellApiToAi.Converter.get(subAb).chkDrawbackWithSubs(ai, subAb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -773,7 +773,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final AbilitySub subAb = sa.getSubAbility();
|
final AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
|
return subAb == null || SpellApiToAi.Converter.get(subAb).chkDrawbackWithSubs(ai, subAb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -821,7 +821,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//don't unearth after attacking is possible
|
//don't unearth after attacking is possible
|
||||||
if (sa.hasParam("Unearth") && ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (sa.isKeyword(Keyword.UNEARTH) && ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,6 +941,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
immediately = immediately || ComputerUtil.playImmediately(ai, sa);
|
immediately = immediately || ComputerUtil.playImmediately(ai, sa);
|
||||||
|
|
||||||
|
if (list.isEmpty() && immediately && sa.getMaxTargets() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Narrow down the list:
|
// Narrow down the list:
|
||||||
if (origin.contains(ZoneType.Battlefield)) {
|
if (origin.contains(ZoneType.Battlefield)) {
|
||||||
if ("Polymorph".equals(sa.getParam("AILogic"))) {
|
if ("Polymorph".equals(sa.getParam("AILogic"))) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
} else if ("Pump".equals(aiLogic) || "BestOption".equals(aiLogic)) {
|
} else if ("Pump".equals(aiLogic) || "BestOption".equals(aiLogic)) {
|
||||||
for (AbilitySub sb : sa.getAdditionalAbilityList("Choices")) {
|
for (AbilitySub sb : sa.getAdditionalAbilityList("Choices")) {
|
||||||
if (SpellApiToAi.Converter.get(sb.getApi()).canPlayAIWithSubs(ai, sb)) {
|
if (SpellApiToAi.Converter.get(sb).canPlayAIWithSubs(ai, sb)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
|||||||
String unlessCost = sp.getParam("UnlessCost");
|
String unlessCost = sp.getParam("UnlessCost");
|
||||||
sp.setActivatingPlayer(sa.getActivatingPlayer());
|
sp.setActivatingPlayer(sa.getActivatingPlayer());
|
||||||
Cost unless = new Cost(unlessCost, false);
|
Cost unless = new Cost(unlessCost, false);
|
||||||
if (SpellApiToAi.Converter.get(sp.getApi()).willPayUnlessCost(sp, player, unless, false, new FCollection<>(player))
|
if (SpellApiToAi.Converter.get(sp).willPayUnlessCost(sp, player, unless, false, new FCollection<>(player))
|
||||||
&& ComputerUtilCost.canPayCost(unless, sp, player, true)) {
|
&& ComputerUtilCost.canPayCost(unless, sp, player, true)) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
@@ -262,7 +262,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
|||||||
List<SpellAbility> filtered = Lists.newArrayList();
|
List<SpellAbility> filtered = Lists.newArrayList();
|
||||||
// filter first for the spells which can be done
|
// filter first for the spells which can be done
|
||||||
for (SpellAbility sp : spells) {
|
for (SpellAbility sp : spells) {
|
||||||
if (SpellApiToAi.Converter.get(sp.getApi()).canPlayAIWithSubs(player, sp)) {
|
if (SpellApiToAi.Converter.get(sp).canPlayAIWithSubs(player, sp)) {
|
||||||
filtered.add(sp);
|
filtered.add(sp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class ClassLevelUpAi extends SpellAbilityAi {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpellAbility effect = t.ensureAbility();
|
SpellAbility effect = t.ensureAbility();
|
||||||
if (!SpellApiToAi.Converter.get(effect.getApi()).doTriggerAI(aiPlayer, effect, false)) {
|
if (!SpellApiToAi.Converter.get(effect).doTriggerAI(aiPlayer, effect, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,6 +255,9 @@ public class CounterAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
if (mandatory && !sa.canAddMoreTarget()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Pair<SpellAbility, Boolean> pair = chooseTargetSpellAbility(game, sa, ai, mandatory);
|
Pair<SpellAbility, Boolean> pair = chooseTargetSpellAbility(game, sa, ai, mandatory);
|
||||||
SpellAbility tgtSA = pair.getLeft();
|
SpellAbility tgtSA = pair.getLeft();
|
||||||
|
|
||||||
@@ -378,7 +381,7 @@ public class CounterAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no reason to pay if we don't plan to confirm
|
// no reason to pay if we don't plan to confirm
|
||||||
if (toBeCountered.isOptionalTrigger() && !SpellApiToAi.Converter.get(toBeCountered.getApi()).doTriggerNoCostWithSubs(payer, toBeCountered, false)) {
|
if (toBeCountered.isOptionalTrigger() && !SpellApiToAi.Converter.get(toBeCountered).doTriggerNoCostWithSubs(payer, toBeCountered, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO check hasFizzled
|
// TODO check hasFizzled
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
|||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (trigsa instanceof AbilitySub) {
|
if (trigsa instanceof AbilitySub) {
|
||||||
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
return SpellApiToAi.Converter.get(trigsa).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||||
} else {
|
} else {
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
} else if (logic.equals("Burn")) {
|
} else if (logic.equals("Burn")) {
|
||||||
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
||||||
SpellAbility burn = sa.getSubAbility();
|
SpellAbility burn = sa.getSubAbility();
|
||||||
return SpellApiToAi.Converter.get(burn.getApi()).canPlayAIWithSubs(ai, burn);
|
return SpellApiToAi.Converter.get(burn).canPlayAIWithSubs(ai, burn);
|
||||||
} else if (logic.equals("YawgmothsWill")) {
|
} else if (logic.equals("YawgmothsWill")) {
|
||||||
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
|
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
|
||||||
} else if (logic.startsWith("NeedCreatures")) {
|
} else if (logic.startsWith("NeedCreatures")) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
|||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (trigsa instanceof AbilitySub) {
|
if (trigsa instanceof AbilitySub) {
|
||||||
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
return SpellApiToAi.Converter.get(trigsa).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||||
} else {
|
} else {
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public class PeekAndRevealAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AbilitySub subAb = sa.getSubAbility();
|
AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
|
return subAb != null && SpellApiToAi.Converter.get(subAb).chkDrawbackWithSubs(player, subAb);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
final AbilitySub sub = sa.getSubAbility();
|
final AbilitySub sub = sa.getSubAbility();
|
||||||
// useful
|
// useful
|
||||||
// no token created
|
// no token created
|
||||||
return pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai)); // planeswalker plus ability or sub-ability is
|
return pwPlus || (sub != null && SpellApiToAi.Converter.get(sub).chkAIDrawback(sub, ai)); // planeswalker plus ability or sub-ability is
|
||||||
}
|
}
|
||||||
|
|
||||||
String tokenPower = sa.getParamOrDefault("TokenPower", actualToken.getBasePowerString());
|
String tokenPower = sa.getParamOrDefault("TokenPower", actualToken.getBasePowerString());
|
||||||
|
|||||||
@@ -1366,7 +1366,7 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.hasKeyword(Keyword.GIFT) && sa.isOptionalCostPaid(OptionalCost.PromiseGift)) {
|
if (source.hasKeyword(Keyword.GIFT) && sa.isGiftPromised()) {
|
||||||
game.getAction().checkStaticAbilities();
|
game.getAction().checkStaticAbilities();
|
||||||
// Is AdditionalAbility available from anything here?
|
// Is AdditionalAbility available from anything here?
|
||||||
AbilitySub giftAbility = (AbilitySub) sa.getAdditionalAbility("GiftAbility");
|
AbilitySub giftAbility = (AbilitySub) sa.getAdditionalAbility("GiftAbility");
|
||||||
@@ -2059,6 +2059,9 @@ public class AbilityUtils {
|
|||||||
if (sq[0].startsWith("Kicked")) { // fallback for not spellAbility
|
if (sq[0].startsWith("Kicked")) { // fallback for not spellAbility
|
||||||
return doXMath(calculateAmount(c, sq[!isUnlinkedFromCastSA(ctb, c) && c.getKickerMagnitude() > 0 ? 1 : 2], ctb), expr, c, ctb);
|
return doXMath(calculateAmount(c, sq[!isUnlinkedFromCastSA(ctb, c) && c.getKickerMagnitude() > 0 ? 1 : 2], ctb), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
if (sq[0].startsWith("PromisedGift")) {
|
||||||
|
return doXMath(calculateAmount(c, sq[c.getCastSA() != null && c.getCastSA().isGiftPromised() ? 1 : 2], ctb), expr, c, ctb);
|
||||||
|
}
|
||||||
if (sq[0].startsWith("Escaped")) {
|
if (sq[0].startsWith("Escaped")) {
|
||||||
return doXMath(calculateAmount(c, sq[c.getCastSA() != null && c.getCastSA().isEscape() ? 1 : 2], ctb), expr, c, ctb);
|
return doXMath(calculateAmount(c, sq[c.getCastSA() != null && c.getCastSA().isEscape() ? 1 : 2], ctb), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import forge.game.ability.AbilityUtils;
|
|||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.event.GameEventCombatChanged;
|
import forge.game.event.GameEventCombatChanged;
|
||||||
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.player.*;
|
import forge.game.player.*;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
@@ -675,7 +676,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (movedCard.getZone().equals(originZone)) {
|
if (movedCard.getZone().equals(originZone)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sa.hasParam("Unearth") && movedCard.isInPlay()) {
|
if (sa.isKeyword(Keyword.UNEARTH) && movedCard.isInPlay()) {
|
||||||
movedCard.setUnearthed(true);
|
movedCard.setUnearthed(true);
|
||||||
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false,
|
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false,
|
||||||
game.getNextTimestamp(), null, true);
|
game.getNextTimestamp(), null, true);
|
||||||
|
|||||||
@@ -3704,9 +3704,8 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
String effect = "AB$ ChangeZone | Cost$ " + manacost + " | Defined$ Self" +
|
String effect = "AB$ ChangeZone | Cost$ " + manacost + " | Defined$ Self" +
|
||||||
" | Origin$ Graveyard | Destination$ Battlefield | SorcerySpeed$ True" +
|
" | Origin$ Graveyard | Destination$ Battlefield | SorcerySpeed$ True" +
|
||||||
" | ActivationZone$ Graveyard | Unearth$ True | " +
|
" | ActivationZone$ Graveyard | PrecostDesc$ Unearth | CostDesc$ " + ManaCostParser.parse(manacost)
|
||||||
" | PrecostDesc$ Unearth | CostDesc$ " + ManaCostParser.parse(manacost) + " | StackDescription$ " +
|
+ " | StackDescription$ Unearth: Return CARDNAME to the battlefield. | SpellDescription$" +
|
||||||
"Unearth: Return CARDNAME to the battlefield. | SpellDescription$" +
|
|
||||||
" (" + inst.getReminderText() + ")";
|
" (" + inst.getReminderText() + ")";
|
||||||
|
|
||||||
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
||||||
|
|||||||
@@ -1826,7 +1826,7 @@ public class CardProperty {
|
|||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isOptionalCostPaid(OptionalCost.PromiseGift);
|
return card.getCastSA().isGiftPromised();
|
||||||
} else if (property.equals("impended")) {
|
} else if (property.equals("impended")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -625,6 +625,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return isOptionalCostPaid(OptionalCost.Jumpstart);
|
return isOptionalCostPaid(OptionalCost.Jumpstart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGiftPromised() {
|
||||||
|
return isOptionalCostPaid(OptionalCost.PromiseGift);
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean isBestow() {
|
public final boolean isBestow() {
|
||||||
return isAlternativeCost(AlternativeCost.Bestow);
|
return isAlternativeCost(AlternativeCost.Bestow);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ Types:Sorcery
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | SubAbility$ DBCopy | RememberChanged$ True | SpellDescription$ Return target creature card from your graveyard to the battlefield. Then if the gift was promised and that creature isn't legendary, create a token that's a copy of that creature, except it's 1/1.
|
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | SubAbility$ DBCopy | RememberChanged$ True | SpellDescription$ Return target creature card from your graveyard to the battlefield. Then if the gift was promised and that creature isn't legendary, create a token that's a copy of that creature, except it's 1/1.
|
||||||
SVar:DBCopy:DB$ CopyPermanent | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ2 | Defined$ Remembered | SetPower$ 1 | SetToughness$ 1 | SubAbility$ DBCleanup
|
SVar:DBCopy:DB$ CopyPermanent | ConditionCheckSVar$ X | ConditionDefined$ Remembered | ConditionPresent$ Card.nonLegendary | Defined$ Remembered | SetPower$ 1 | SetToughness$ 1 | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift/Plus.Y
|
SVar:X:Count$PromisedGift.1.0
|
||||||
SVar:Y:Count$Valid Card.IsRemembered+nonLegendary
|
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn target creature card from your graveyard to the battlefield. Then if the gift was promised and that creature isn't legendary, create a token that's a copy of that creature, except it's 1/1.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn target creature card from your graveyard to the battlefield. Then if the gift was promised and that creature isn't legendary, create a token that's a copy of that creature, except it's 1/1.
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ K:Gift
|
|||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ Sacrifice | ValidTgts$ Opponent | SacValid$ Creature.greatestPowerControlledByTargeted | SacMessage$ the creature with the greatest power | SubAbility$ DBChangeZone | SpellDescription$ Target opponent sacrifices a creature with the greatest power among creatures they control. If the gift was promised, return target creature card from your graveyard to your hand.
|
A:SP$ Sacrifice | ValidTgts$ Opponent | SacValid$ Creature.greatestPowerControlledByTargeted | SacMessage$ the creature with the greatest power | SubAbility$ DBChangeZone | SpellDescription$ Target opponent sacrifices a creature with the greatest power among creatures they control. If the gift was promised, return target creature card from your graveyard to your hand.
|
||||||
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Choose target creature card in your graveyard | TargetMin$ X | TargetMax$ X | ValidTgts$ Creature.YouOwn
|
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Choose target creature card in your graveyard | TargetMin$ X | TargetMax$ X | ValidTgts$ Creature.YouOwn
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift
|
SVar:X:Count$PromisedGift.1.0
|
||||||
DeckHas:Ability$Graveyard
|
DeckHas:Ability$Graveyard
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nTarget opponent sacrifices a creature with the greatest power among creatures they control. If the gift was promised, return target creature card from your graveyard to your hand.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nTarget opponent sacrifices a creature with the greatest power among creatures they control. If the gift was promised, return target creature card from your graveyard to your hand.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Sorcery
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card with mana value 2 or less in your graveyard | ValidTgts$ Creature.YouOwn+cmcLE2 | TargetMin$ 0 | TargetMax$ X | SpellDescription$ Return up to two target creature cards each with mana value 2 or less from your graveyard to the battlefield. If the gift was promised, instead return up to three target creature cards each with mana value 2 or less from your graveyard to the battlefield.
|
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card with mana value 2 or less in your graveyard | ValidTgts$ Creature.YouOwn+cmcLE2 | TargetMin$ 0 | TargetMax$ X | SpellDescription$ Return up to two target creature cards each with mana value 2 or less from your graveyard to the battlefield. If the gift was promised, instead return up to three target creature cards each with mana value 2 or less from your graveyard to the battlefield.
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift/Plus.2
|
SVar:X:Count$PromisedGift.3.2
|
||||||
DeckHints:Ability$Graveyard|Discard
|
DeckHints:Ability$Graveyard|Discard
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn up to two target creature cards each with mana value 2 or less from your graveyard to the battlefield. If the gift was promised, instead return up to three target creature cards each with mana value 2 or less from your graveyard to the battlefield.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn up to two target creature cards each with mana value 2 or less from your graveyard to the battlefield. If the gift was promised, instead return up to three target creature cards each with mana value 2 or less from your graveyard to the battlefield.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Instant
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_fish | TokenTapped$ True | TokenOwner$ Promised | LockTokenScript$ True | GiftDescription$ a tapped Fish
|
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_fish | TokenTapped$ True | TokenOwner$ Promised | LockTokenScript$ True | GiftDescription$ a tapped Fish
|
||||||
A:SP$ ChangeZone | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Origin$ Battlefield | Destination$ Hand | TargetMin$ X | TargetMax$ X | SubAbility$ DBChangeZone | SpellDescription$ Return target creature an opponent controls to its owner's hand. If the gift was promised, instead return target nonland permanent an opponent controls to its owner's hand.
|
A:SP$ ChangeZone | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Origin$ Battlefield | Destination$ Hand | TargetMin$ X | TargetMax$ X | SubAbility$ DBChangeZone | SpellDescription$ Return target creature an opponent controls to its owner's hand. If the gift was promised, instead return target nonland permanent an opponent controls to its owner's hand.
|
||||||
SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | Origin$ Battlefield | Destination$ Hand | TargetMin$ Y | TargetMax$ Y
|
SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | Origin$ Battlefield | Destination$ Hand | TargetMin$ Y | TargetMax$ Y | AITgts$ !Creature
|
||||||
SVar:X:Count$Compare Y EQ1.0.1
|
SVar:X:Count$PromisedGift.0.1
|
||||||
SVar:Y:Count$ValidStack Card.Self+PromisedGift
|
SVar:Y:Count$PromisedGift.1.0
|
||||||
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nReturn target creature an opponent controls to its owner's hand. If the gift was promised, instead return target nonland permanent an opponent controls to its owner's hand.
|
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nReturn target creature an opponent controls to its owner's hand. If the gift was promised, instead return target nonland permanent an opponent controls to its owner's hand.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Instant
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target creature spell | ValidTgts$ Creature | TargetMin$ X | TargetMax$ X | SubAbility$ DBCounter | SpellDescription$ Counter target creature spell. If the gift was promised, instead counter target spell.
|
A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target creature spell | ValidTgts$ Creature | TargetMin$ X | TargetMax$ X | SubAbility$ DBCounter | SpellDescription$ Counter target creature spell. If the gift was promised, instead counter target spell.
|
||||||
SVar:DBCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | TargetMin$ Y | TargetMax$ Y
|
SVar:DBCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | TargetMin$ Y | TargetMax$ Y | AITgts$ !Creature
|
||||||
SVar:X:Count$Compare Y EQ1.0.1
|
SVar:X:Count$PromisedGift.0.1
|
||||||
SVar:Y:Count$ValidStack Card.Self+PromisedGift
|
SVar:Y:Count$PromisedGift.1.0
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nCounter target creature spell. If the gift was promised, instead counter target spell.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nCounter target creature spell. If the gift was promised, instead counter target spell.
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_fish | TokenTap
|
|||||||
A:SP$ Draw | NumCards$ 3 | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBTap | SpellDescription$ Target player draws three cards. If the gift was promised, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
A:SP$ Draw | NumCards$ 3 | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBTap | SpellDescription$ Target player draws three cards. If the gift was promised, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
||||||
SVar:DBTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TargetMin$ X | TargetMax$ X | SubAbility$ DBCounter
|
SVar:DBTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TargetMin$ X | TargetMax$ X | SubAbility$ DBCounter
|
||||||
SVar:DBCounter:DB$ PutCounter | Defined$ ParentTarget | ConditionZone$ Stack | ConditionPresent$ Card.Self+PromisedGift | ConditionCompare$ EQ1 | CounterType$ Stun | CounterNum$ 1
|
SVar:DBCounter:DB$ PutCounter | Defined$ ParentTarget | ConditionZone$ Stack | ConditionPresent$ Card.Self+PromisedGift | ConditionCompare$ EQ1 | CounterType$ Stun | CounterNum$ 1
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift
|
SVar:X:Count$PromisedGift.1.0
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nTarget player draws three cards. If the gift was promised, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nTarget player draws three cards. If the gift was promised, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ Types:Instant
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TargetMin$ X | TargetMax$ X | TgtPrompt$ Choose target permanent card in your graveyard | ValidTgts$ Permanent.YouCtrl | SpellDescription$ Return target permanent card from your graveyard to your hand. If the gift was promised, instead return two target permanent cards from your graveyard to your hand.
|
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TargetMin$ X | TargetMax$ X | TgtPrompt$ Choose target permanent card in your graveyard | ValidTgts$ Permanent.YouCtrl | SpellDescription$ Return target permanent card from your graveyard to your hand. If the gift was promised, instead return two target permanent cards from your graveyard to your hand.
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift/Plus.1
|
SVar:X:Count$PromisedGift.2.1
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn target permanent card from your graveyard to your hand. If the gift was promised, instead return two target permanent cards from your graveyard to your hand.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nReturn target permanent card from your graveyard to your hand. If the gift was promised, instead return two target permanent cards from your graveyard to your hand.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ K:Gift
|
|||||||
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_fish | TokenTapped$ True | TokenOwner$ Promised | LockTokenScript$ True | GiftDescription$ a tapped Fish
|
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_fish | TokenTapped$ True | TokenOwner$ Promised | LockTokenScript$ True | GiftDescription$ a tapped Fish
|
||||||
A:SP$ Draw | Cost$ 1 R Discard<1/Card> | CostDesc$ As an additional cost to cast this spell, discard a card. | NumCards$ 2 | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBPump | SpellDescription$ Target player draws two cards. If the gift was promised, target creature you control gets +2/+0 until end of turn.
|
A:SP$ Draw | Cost$ 1 R Discard<1/Card> | CostDesc$ As an additional cost to cast this spell, discard a card. | NumCards$ 2 | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBPump | SpellDescription$ Target player draws two cards. If the gift was promised, target creature you control gets +2/+0 until end of turn.
|
||||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target creature you control | NumAtt$ +2
|
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target creature you control | NumAtt$ +2
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift
|
SVar:X:Count$PromisedGift.1.0
|
||||||
DeckHas:Ability$Discard
|
DeckHas:Ability$Discard
|
||||||
DeckHints:Keyword$Madness & Ability$Delirium
|
DeckHints:Keyword$Madness & Ability$Delirium
|
||||||
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nAs an additional cost to cast this spell, discard a card.\nTarget player draws two cards. If the gift was promised, target creature you control gets +2/+0 until end of turn.
|
Oracle:Gift a tapped Fish (You may promise an opponent a gift as you cast this spell. If you do, they create a tapped 1/1 blue Fish creature token before its other effects.)\nAs an additional cost to cast this spell, discard a card.\nTarget player draws two cards. If the gift was promised, target creature you control gets +2/+0 until end of turn.
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ K:Gift
|
|||||||
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ Promised | GiftDescription$ a Food
|
SVar:GiftAbility:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ Promised | GiftDescription$ a Food
|
||||||
A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | SubAbility$ DBPump | SpellDescription$ Creatures you control get +2/+0 until end of turn. If the gift was promised, target creature you control gains first strike until end of turn.
|
A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | SubAbility$ DBPump | SpellDescription$ Creatures you control get +2/+0 until end of turn. If the gift was promised, target creature you control gains first strike until end of turn.
|
||||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target creature you control | KW$ First Strike
|
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target creature you control | KW$ First Strike
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift
|
SVar:X:Count$PromisedGift.1.0
|
||||||
Oracle:Gift a Food (You may promise an opponent a gift as you cast this spell. If you do, they create a Food token before its other effects. It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")\nCreatures you control get +2/+0 until end of turn. If the gift was promised, target creature you control gains first strike until end of turn.
|
Oracle:Gift a Food (You may promise an opponent a gift as you cast this spell. If you do, they create a Food token before its other effects. It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")\nCreatures you control get +2/+0 until end of turn. If the gift was promised, target creature you control gains first strike until end of turn.
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ Types:Sorcery
|
|||||||
K:Gift
|
K:Gift
|
||||||
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a card
|
||||||
A:SP$ Destroy | ValidTgts$ Artifact,Enchantment | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target artifact or enchantment | SpellDescription$ Destroy target artifact or enchantment. If the gift was promised, instead destroy two target artifacts and/or enchantments.
|
A:SP$ Destroy | ValidTgts$ Artifact,Enchantment | TargetMin$ X | TargetMax$ X | TgtPrompt$ Select target artifact or enchantment | SpellDescription$ Destroy target artifact or enchantment. If the gift was promised, instead destroy two target artifacts and/or enchantments.
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift/Plus.1
|
SVar:X:Count$PromisedGift.2.1
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nDestroy target artifact or enchantment. If the gift was promised, instead destroy two target artifacts and/or enchantments.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nDestroy target artifact or enchantment. If the gift was promised, instead destroy two target artifacts and/or enchantments.
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ SVar:GiftAbility:DB$ Draw | NumCards$ 1 | Defined$ Promised | GiftDescription$ a
|
|||||||
A:SP$ DealDamage | ValidTgts$ Any | NumDmg$ 1 | TargetMin$ X | TargetMax$ X | SubAbility$ DBDamageAll | DamageMap$ True | SpellDescription$ CARDNAME deals 2 damage to each creature. If the gift was promised, instead CARDNAME deals 1 damage to any target and 2 damage to each creature.
|
A:SP$ DealDamage | ValidTgts$ Any | NumDmg$ 1 | TargetMin$ X | TargetMax$ X | SubAbility$ DBDamageAll | DamageMap$ True | SpellDescription$ CARDNAME deals 2 damage to each creature. If the gift was promised, instead CARDNAME deals 1 damage to any target and 2 damage to each creature.
|
||||||
SVar:DBDamageAll:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature | ValidDescription$ each creature | SubAbility$ DBDamageResolve
|
SVar:DBDamageAll:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature | ValidDescription$ each creature | SubAbility$ DBDamageResolve
|
||||||
SVar:DBDamageResolve:DB$ DamageResolve
|
SVar:DBDamageResolve:DB$ DamageResolve
|
||||||
SVar:X:Count$ValidStack Card.Self+PromisedGift
|
SVar:X:Count$PromisedGift.1.0
|
||||||
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nWildfire Howl deals 2 damage to each creature. If the gift was promised, instead Wildfire Howl deals 1 damage to any target and 2 damage to each creature.
|
Oracle:Gift a card (You may promise an opponent a gift as you cast this spell. If you do, they draw a card before its other effects.)\nWildfire Howl deals 2 damage to each creature. If the gift was promised, instead Wildfire Howl deals 1 damage to any target and 2 damage to each creature.
|
||||||
|
|||||||
Reference in New Issue
Block a user