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) {
|
||||
host.setState(sa.getCardStateName(), false);
|
||||
}
|
||||
if (sa.isSpell()) {
|
||||
host.setCastSA(sa);
|
||||
}
|
||||
|
||||
AiPlayDecision decision = canPlayAndPayForFace(sa);
|
||||
|
||||
if (sa.isSpell()) {
|
||||
host.setCastSA(null);
|
||||
}
|
||||
if (currentState != null) {
|
||||
host.setState(currentState, false);
|
||||
}
|
||||
@@ -918,7 +924,7 @@ public class AiController {
|
||||
Sentry.setExtra("Card", card.getName());
|
||||
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
|
||||
Sentry.removeExtra("Card");
|
||||
@@ -1296,9 +1302,9 @@ public class AiController {
|
||||
if (spell instanceof SpellApiBased) {
|
||||
boolean chance = false;
|
||||
if (withoutPayingManaCost) {
|
||||
chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerNoCostWithSubs(player, spell, mandatory);
|
||||
chance = SpellApiToAi.Converter.get(spell).doTriggerNoCostWithSubs(player, spell, mandatory);
|
||||
} else {
|
||||
chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
||||
chance = SpellApiToAi.Converter.get(spell).doTriggerAI(player, spell, mandatory);
|
||||
}
|
||||
if (!chance) {
|
||||
return AiPlayDecision.TargetingFailed;
|
||||
@@ -1630,28 +1636,35 @@ public class AiController {
|
||||
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 (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) {
|
||||
&& !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 (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
|
||||
continue;
|
||||
else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && 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 if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player)
|
||||
&& 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 {
|
||||
continue;
|
||||
}
|
||||
@@ -1726,7 +1739,7 @@ public class AiController {
|
||||
if (spell instanceof WrappedAbility)
|
||||
return doTrigger(((WrappedAbility) spell).getWrappedAbility(), mandatory);
|
||||
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()) {
|
||||
// For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about
|
||||
return true;
|
||||
|
||||
@@ -906,7 +906,7 @@ public class ComputerUtil {
|
||||
|
||||
// Run non-mandatory trigger.
|
||||
// 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
|
||||
return sacrificed;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
@@ -122,6 +123,10 @@ public class ComputerUtilAbility {
|
||||
boolean choseOptCost = false;
|
||||
List<OptionalCostValue> list = GameActionUtil.getOptionalCostValues(sa);
|
||||
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);
|
||||
if (!list.isEmpty()) {
|
||||
choseOptCost = true;
|
||||
|
||||
@@ -1491,7 +1491,7 @@ public class ComputerUtilMana {
|
||||
AbilitySub sub = m.getSubAbility();
|
||||
// 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 (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
|
||||
if (!SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub)) {
|
||||
continue;
|
||||
}
|
||||
needsLimitedResources = true; // TODO: check for good drawbacks (gainLife)
|
||||
@@ -1571,7 +1571,7 @@ public class ComputerUtilMana {
|
||||
// don't use abilities with dangerous drawbacks
|
||||
AbilitySub sub = m.getSubAbility();
|
||||
if (sub != null) {
|
||||
if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
|
||||
if (!SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -352,11 +351,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
if (delayedReveal != null) {
|
||||
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||
}
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -398,11 +393,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
@Override
|
||||
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
||||
Map<String, Object> params) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseSingleSpellAbility(player, sa, spells, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -876,11 +867,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseNumber(player, sa, min, max, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -982,11 +969,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
*/
|
||||
@Override
|
||||
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseBinary(kindOfChoice, sa, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1056,11 +1039,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
if (options.size() <= 1) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseCounterType(options, sa, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1217,7 +1196,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1397,11 +1376,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseCardName(player, sa, faces);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1506,11 +1481,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public ICardFace chooseSingleCardFace(SpellAbility sa, List<ICardFace> faces, String message) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseCardFace(player, sa, faces);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1520,11 +1491,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public CardState chooseSingleCardState(SpellAbility sa, List<CardState> states, String message, Map<String, Object> params) {
|
||||
ApiType api = sa.getApi();
|
||||
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);
|
||||
return SpellApiToAi.Converter.get(sa).chooseCardState(player, sa, states, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1576,32 +1543,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, 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;
|
||||
return SpellApiToAi.Converter.get(chosen).chooseOptionalCosts(chosen, player, optionalCostValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1661,5 +1603,4 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCopyService;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.Cost;
|
||||
@@ -23,6 +24,8 @@ import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.OptionalCostValue;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityCondition;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -305,7 +308,7 @@ public abstract class SpellAbilityAi {
|
||||
*/
|
||||
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
|
||||
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) {
|
||||
@@ -410,4 +413,33 @@ public abstract class SpellAbilityAi {
|
||||
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
||||
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 forge.ai.ability.*;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.ReflectionUtil;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Map;
|
||||
|
||||
public enum SpellApiToAi {
|
||||
@@ -207,6 +209,14 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.InternalRadiation, AlwaysPlayAi.class)
|
||||
.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) {
|
||||
SpellAbilityAi result = apiToInstance.get(api);
|
||||
if (null == result) {
|
||||
|
||||
@@ -455,7 +455,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
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();
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -941,6 +941,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
immediately = immediately || ComputerUtil.playImmediately(ai, sa);
|
||||
|
||||
if (list.isEmpty() && immediately && sa.getMaxTargets() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Narrow down the list:
|
||||
if (origin.contains(ZoneType.Battlefield)) {
|
||||
if ("Polymorph".equals(sa.getParam("AILogic"))) {
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
||||
return true;
|
||||
} else if ("Pump".equals(aiLogic) || "BestOption".equals(aiLogic)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
||||
String unlessCost = sp.getParam("UnlessCost");
|
||||
sp.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
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)) {
|
||||
return sp;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
|
||||
List<SpellAbility> filtered = Lists.newArrayList();
|
||||
// filter first for the spells which can be done
|
||||
for (SpellAbility sp : spells) {
|
||||
if (SpellApiToAi.Converter.get(sp.getApi()).canPlayAIWithSubs(player, sp)) {
|
||||
if (SpellApiToAi.Converter.get(sp).canPlayAIWithSubs(player, sp)) {
|
||||
filtered.add(sp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class ClassLevelUpAi extends SpellAbilityAi {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +255,9 @@ public class CounterAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
sa.resetTargets();
|
||||
if (mandatory && !sa.canAddMoreTarget()) {
|
||||
return true;
|
||||
}
|
||||
Pair<SpellAbility, Boolean> pair = chooseTargetSpellAbility(game, sa, ai, mandatory);
|
||||
SpellAbility tgtSA = pair.getLeft();
|
||||
|
||||
@@ -378,7 +381,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// TODO check hasFizzled
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
|
||||
if (trigsa instanceof AbilitySub) {
|
||||
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||
return SpellApiToAi.Converter.get(trigsa).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||
} else {
|
||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ public class EffectAi extends SpellAbilityAi {
|
||||
} else if (logic.equals("Burn")) {
|
||||
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
||||
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")) {
|
||||
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
|
||||
} else if (logic.startsWith("NeedCreatures")) {
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
|
||||
if (trigsa instanceof AbilitySub) {
|
||||
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||
return SpellApiToAi.Converter.get(trigsa).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||
} else {
|
||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class PeekAndRevealAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
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();
|
||||
// useful
|
||||
// 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());
|
||||
|
||||
@@ -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();
|
||||
// Is AdditionalAbility available from anything here?
|
||||
AbilitySub giftAbility = (AbilitySub) sa.getAdditionalAbility("GiftAbility");
|
||||
@@ -2059,6 +2059,9 @@ public class AbilityUtils {
|
||||
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);
|
||||
}
|
||||
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")) {
|
||||
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.card.*;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.*;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
@@ -675,7 +676,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (movedCard.getZone().equals(originZone)) {
|
||||
continue;
|
||||
}
|
||||
if (sa.hasParam("Unearth") && movedCard.isInPlay()) {
|
||||
if (sa.isKeyword(Keyword.UNEARTH) && movedCard.isInPlay()) {
|
||||
movedCard.setUnearthed(true);
|
||||
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false,
|
||||
game.getNextTimestamp(), null, true);
|
||||
|
||||
@@ -3704,9 +3704,8 @@ public class CardFactoryUtil {
|
||||
|
||||
String effect = "AB$ ChangeZone | Cost$ " + manacost + " | Defined$ Self" +
|
||||
" | Origin$ Graveyard | Destination$ Battlefield | SorcerySpeed$ True" +
|
||||
" | ActivationZone$ Graveyard | Unearth$ True | " +
|
||||
" | PrecostDesc$ Unearth | CostDesc$ " + ManaCostParser.parse(manacost) + " | StackDescription$ " +
|
||||
"Unearth: Return CARDNAME to the battlefield. | SpellDescription$" +
|
||||
" | ActivationZone$ Graveyard | PrecostDesc$ Unearth | CostDesc$ " + ManaCostParser.parse(manacost)
|
||||
+ " | StackDescription$ Unearth: Return CARDNAME to the battlefield. | SpellDescription$" +
|
||||
" (" + inst.getReminderText() + ")";
|
||||
|
||||
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
||||
|
||||
@@ -1826,7 +1826,7 @@ public class CardProperty {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isOptionalCostPaid(OptionalCost.PromiseGift);
|
||||
return card.getCastSA().isGiftPromised();
|
||||
} else if (property.equals("impended")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
|
||||
@@ -625,6 +625,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return isOptionalCostPaid(OptionalCost.Jumpstart);
|
||||
}
|
||||
|
||||
public boolean isGiftPromised() {
|
||||
return isOptionalCostPaid(OptionalCost.PromiseGift);
|
||||
}
|
||||
|
||||
public final boolean isBestow() {
|
||||
return isAlternativeCost(AlternativeCost.Bestow);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ Types:Sorcery
|
||||
K:Gift
|
||||
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.
|
||||
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:X:Count$ValidStack Card.Self+PromisedGift/Plus.Y
|
||||
SVar:Y:Count$Valid Card.IsRemembered+nonLegendary
|
||||
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.)\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
|
||||
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:X:Count$ValidStack Card.Self+PromisedGift
|
||||
SVar:X:Count$PromisedGift.1.0
|
||||
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.
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Sorcery
|
||||
K:Gift
|
||||
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.
|
||||
SVar:X:Count$ValidStack Card.Self+PromisedGift/Plus.2
|
||||
SVar:X:Count$PromisedGift.3.2
|
||||
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.
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Instant
|
||||
K:Gift
|
||||
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.
|
||||
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:X:Count$Compare Y EQ1.0.1
|
||||
SVar:Y:Count$ValidStack Card.Self+PromisedGift
|
||||
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$PromisedGift.0.1
|
||||
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.
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Instant
|
||||
K:Gift
|
||||
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.
|
||||
SVar:DBCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | TargetMin$ Y | TargetMax$ Y
|
||||
SVar:X:Count$Compare Y EQ1.0.1
|
||||
SVar:Y:Count$ValidStack Card.Self+PromisedGift
|
||||
SVar:DBCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | TargetMin$ Y | TargetMax$ Y | AITgts$ !Creature
|
||||
SVar:X:Count$PromisedGift.0.1
|
||||
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.
|
||||
|
||||
@@ -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.)
|
||||
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:X:Count$ValidStack Card.Self+PromisedGift
|
||||
SVar:X:Count$PromisedGift.1.0
|
||||
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.)
|
||||
|
||||
@@ -4,5 +4,5 @@ Types:Instant
|
||||
K:Gift
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
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:X:Count$ValidStack Card.Self+PromisedGift
|
||||
SVar:X:Count$PromisedGift.1.0
|
||||
DeckHas:Ability$Discard
|
||||
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.
|
||||
|
||||
@@ -5,5 +5,5 @@ K:Gift
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -4,5 +4,5 @@ Types:Sorcery
|
||||
K:Gift
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
SVar:DBDamageAll:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature | ValidDescription$ each creature | SubAbility$ DBDamageResolve
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user