mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
Merge branch 'mdfc' into 'master'
Correctly evaluate modal faces See merge request core-developers/forge!5112
This commit is contained in:
@@ -750,7 +750,15 @@ public class AiController {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
|
||||
// state needs to be switched here so API checks evaluate the right face
|
||||
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
sa.getHostCard().setState(CardStateName.Modal, false);
|
||||
}
|
||||
AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
sa.getHostCard().setState(CardStateName.Original, false);
|
||||
}
|
||||
|
||||
if (canPlay != AiPlayDecision.WillPlay) {
|
||||
return canPlay;
|
||||
}
|
||||
@@ -810,8 +818,7 @@ public class AiController {
|
||||
if (!canPlay) {
|
||||
return AiPlayDecision.CantPlayAi;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Cost payCosts = sa.getPayCosts();
|
||||
if (payCosts != null) {
|
||||
ManaCost mana = payCosts.getTotalMana();
|
||||
|
||||
@@ -484,7 +484,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(source);
|
||||
}
|
||||
if (cost.getType().equals("OriginalHost")) {
|
||||
return PaymentDecision.card(ability.getHostCard());
|
||||
return PaymentDecision.card(ability.getOriginalHost());
|
||||
}
|
||||
if (cost.getAmount().equals("All")) {
|
||||
// Does the AI want to use Sacrifice All?
|
||||
@@ -600,7 +600,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
// currently if amount is bigger than one,
|
||||
// it tries to remove all counters from one source and type at once
|
||||
|
||||
|
||||
int toRemove = 0;
|
||||
final GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
|
||||
|
||||
@@ -2890,10 +2890,10 @@ public class ComputerUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
|
||||
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost, boolean mandatory) {
|
||||
// determine and target a card with a SA that the AI can afford and will play
|
||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||
Card targetSpellCard = null;
|
||||
CardCollection targets = new CardCollection();
|
||||
for (Card c : options) {
|
||||
if (withoutPayingManaCost && c.getManaCost() != null && c.getManaCost().countX() > 0) {
|
||||
// The AI will otherwise cheat with the mana payment, announcing X > 0 for spells like Heat Ray when replaying them
|
||||
@@ -2913,18 +2913,19 @@ public class ComputerUtil {
|
||||
// at this point, we're assuming that card will be castable from whichever zone it's in by the AI player.
|
||||
abTest.setActivatingPlayer(ai);
|
||||
abTest.getRestrictions().setZone(c.getZone().getZoneType());
|
||||
final boolean play = AiPlayDecision.WillPlay == aic.canPlaySa(abTest);
|
||||
final boolean pay = ComputerUtilCost.canPayCost(abTest, ai);
|
||||
if (play && pay) {
|
||||
targetSpellCard = c;
|
||||
break;
|
||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(abTest) && ComputerUtilCost.canPayCost(abTest, ai)) {
|
||||
targets.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetSpellCard == null) {
|
||||
return false;
|
||||
if (targets.isEmpty()) {
|
||||
if (mandatory && !options.isEmpty()) {
|
||||
targets = options;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sa.getTargets().add(targetSpellCard);
|
||||
sa.getTargets().add(ComputerUtilCard.getBestAI(targets));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -633,7 +633,7 @@ public class ComputerUtilCost {
|
||||
}
|
||||
// Check if the AI intends to play the card and if it can pay for it with the mana it has
|
||||
boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c);
|
||||
boolean canPay = c.getManaCost().canBePaidWithAvaliable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
|
||||
boolean canPay = c.getManaCost().canBePaidWithAvailable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
|
||||
return canPay && willPlay;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,7 +1099,7 @@ public class SpecialCardAi {
|
||||
|
||||
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
||||
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvailable(ColorSet.fromNames(
|
||||
ComputerUtilCost.getAvailableManaColors(ai, sa.getHostCard())).getColor());
|
||||
|
||||
byte colorProfile = cost.getColorProfile();
|
||||
|
||||
@@ -233,10 +233,10 @@ public class EffectAi extends SpellAbilityAi {
|
||||
return ai.getCreaturesInPlay().size() >= i;
|
||||
}
|
||||
return true;
|
||||
} else if (logic.equals("CastFromGraveThisTurn")) {
|
||||
} else if (logic.equals("ReplaySpell")) {
|
||||
CardCollection list = new CardCollection(game.getCardsIn(ZoneType.Graveyard));
|
||||
list = CardLists.getValidCards(list, sa.getTargetRestrictions().getValidTgts(), ai, sa.getHostCard(), sa);
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false, false)) {
|
||||
return false;
|
||||
}
|
||||
} else if (logic.equals("Bribe")) {
|
||||
@@ -313,6 +313,5 @@ public class EffectAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(ai.getCardsIn(ZoneType.Hand), ai);
|
||||
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
||||
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvailable(ColorSet.fromNames(
|
||||
ComputerUtilCost.getAvailableManaColors(ai, (List<Card>)null)).getColor());
|
||||
|
||||
if (cost.getCMC() == 0 && cost.countX() == 0) {
|
||||
|
||||
@@ -40,33 +40,9 @@ public class PlayAi extends SpellAbilityAi {
|
||||
return false; // prevent infinite loop
|
||||
}
|
||||
|
||||
CardCollection cards = null;
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
if (tgt != null) {
|
||||
ZoneType zone = tgt.getZone().get(0);
|
||||
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
||||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (!sa.hasParam("Valid")) {
|
||||
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cards != null & sa.hasParam("ValidSA")) {
|
||||
final String valid[] = sa.getParam("ValidSA").split(",");
|
||||
final Iterator<Card> itr = cards.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Card c = itr.next();
|
||||
if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , c, sa))) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
CardCollection cards = getPlayableCards(sa, ai);
|
||||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (game.getRules().hasAppliedVariant(GameType.MoJhoSto) && source.getName().equals("Jhoira of the Ghitu Avatar")) {
|
||||
@@ -85,25 +61,15 @@ public class PlayAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that if a ValidZone is specified, there's at least something to choose from in that zone.
|
||||
CardCollectionView validOpts = new CardCollection();
|
||||
if (sa.hasParam("ValidZone")) {
|
||||
validOpts = AbilityUtils.filterListByType(game.getCardsIn(ZoneType.valueOf(sa.getParam("ValidZone"))),
|
||||
sa.getParam("Valid"), sa);
|
||||
if (validOpts.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ("ReplaySpell".equals(logic)) {
|
||||
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
|
||||
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"), false);
|
||||
} else if (logic.startsWith("NeedsChosenCard")) {
|
||||
int minCMC = 0;
|
||||
if (sa.getPayCosts().getCostMana() != null) {
|
||||
minCMC = sa.getPayCosts().getTotalMana().getCMC();
|
||||
}
|
||||
validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC));
|
||||
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null, null) != null;
|
||||
cards = CardLists.filter(cards, CardPredicates.greaterCMC(minCMC));
|
||||
return chooseSingleCard(ai, sa, cards, sa.hasParam("Optional"), null, null) != null;
|
||||
} else if ("WithTotalCMC".equals(logic)) {
|
||||
// Try to play only when there are more than three playable cards.
|
||||
if (cards.size() < 3)
|
||||
@@ -154,6 +120,10 @@ public class PlayAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("ReplaySpell".equals(sa.getParam("AILogic"))) {
|
||||
return ComputerUtil.targetPlayableSpellCard(ai, getPlayableCards(sa, ai), sa, sa.hasParam("WithoutManaCost"), mandatory);
|
||||
}
|
||||
|
||||
return checkApiLogic(ai, sa);
|
||||
}
|
||||
|
||||
@@ -218,4 +188,36 @@ public class PlayAi extends SpellAbilityAi {
|
||||
});
|
||||
return ComputerUtilCard.getBestAI(tgtCards);
|
||||
}
|
||||
|
||||
private static CardCollection getPlayableCards(SpellAbility sa, Player ai) {
|
||||
CardCollection cards = new CardCollection();
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
if (tgt != null) {
|
||||
ZoneType zone = tgt.getZone().get(0);
|
||||
cards = CardLists.getValidCards(ai.getGame().getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
||||
} else if (!sa.hasParam("Valid")) {
|
||||
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||
}
|
||||
|
||||
if (cards != null & sa.hasParam("ValidSA")) {
|
||||
final String valid[] = sa.getParam("ValidSA").split(",");
|
||||
final Iterator<Card> itr = cards.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Card c = itr.next();
|
||||
if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , c, sa))) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that if a ValidZone is specified, there's at least something to choose from in that zone.
|
||||
if (sa.hasParam("ValidZone")) {
|
||||
cards = new CardCollection(AbilityUtils.filterListByType(ai.getGame().getCardsIn(ZoneType.valueOf(sa.getParam("ValidZone"))),
|
||||
sa.getParam("Valid"), sa));
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -596,7 +596,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
|
||||
if ("Snapcaster".equals(sa.getParam("AILogic"))) {
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
|
||||
uncastableCMCThreshold = aic.getIntProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF);
|
||||
}
|
||||
|
||||
Player p = pc.getFirst(); // FIXME: is this always a single target spell?
|
||||
Player p = pc.getFirst(); // currently always a single target spell
|
||||
Card top = p.getCardsIn(ZoneType.Library).getFirst();
|
||||
int landsOTB = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
||||
int cmc = top.isSplitCard() ? Math.min(top.getCMC(Card.SplitCMCMode.LeftSplitCMC), top.getCMC(Card.SplitCMCMode.RightSplitCMC))
|
||||
|
||||
@@ -182,7 +182,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
//if card face has no cost, assume castable only by mana of its defined color
|
||||
return face.getColor().hasNoColorsExcept(colorCode);
|
||||
}
|
||||
return face.getManaCost().canBePaidWithAvaliable(colorCode);
|
||||
return face.getManaCost().canBePaidWithAvailable(colorCode);
|
||||
}
|
||||
|
||||
public boolean canCastWithAvailable(byte colorCode) {
|
||||
|
||||
@@ -353,7 +353,7 @@ public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostSh
|
||||
* @param colorCode
|
||||
* @return
|
||||
*/
|
||||
public boolean canBePaidWithAvaliable(byte colorCode) {
|
||||
public boolean canBePaidWithAvailable(byte colorCode) {
|
||||
for (ManaCostShard shard : shards) {
|
||||
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
|
||||
return false;
|
||||
|
||||
@@ -82,7 +82,6 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
boolean isOptional = sa.hasParam("Optional");
|
||||
|
||||
for (Player controller : controllers) {
|
||||
|
||||
List<SpellAbility> copies = Lists.newArrayList();
|
||||
|
||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||
|
||||
@@ -6342,7 +6342,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
if (isFaceDown() && isInZone(ZoneType.Exile)) {
|
||||
for (final SpellAbility sa : getState(CardStateName.Original).getSpellAbilities()) {
|
||||
for (final SpellAbility sa : oState.getSpellAbilities()) {
|
||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class CardLists {
|
||||
*/
|
||||
public static void sortByCmcDesc(final List<Card> list) {
|
||||
Collections.sort(list, CmcComparatorInv);
|
||||
} // sortByCmcDesc
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -133,7 +133,7 @@ public class CardLists {
|
||||
*/
|
||||
public static void sortByToughnessAsc(final List<Card> list) {
|
||||
Collections.sort(list, ToughnessComparator);
|
||||
} // sortByToughnessAsc()
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -144,7 +144,7 @@ public class CardLists {
|
||||
*/
|
||||
public static void sortByToughnessDesc(final List<Card> list) {
|
||||
Collections.sort(list, ToughnessComparatorInv);
|
||||
} // sortByToughnessDesc()
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -155,7 +155,7 @@ public class CardLists {
|
||||
*/
|
||||
public static void sortByPowerAsc(final List<Card> list) {
|
||||
Collections.sort(list, PowerComparator);
|
||||
} // sortAttackLowFirst()
|
||||
}
|
||||
|
||||
// the higher the attack the better
|
||||
/**
|
||||
@@ -167,7 +167,7 @@ public class CardLists {
|
||||
*/
|
||||
public static void sortByPowerDesc(final List<Card> list) {
|
||||
Collections.sort(list, Collections.reverseOrder(PowerComparator));
|
||||
} // sortAttack()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,8 +63,6 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
@@ -72,10 +70,8 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private final SpellAbilityEffect effect;
|
||||
|
||||
|
||||
public AbilitySub(ApiType api0, final Card ca, final TargetRestrictions tgt, Map<String, String> params0) {
|
||||
super(ca, Cost.Zero);
|
||||
this.setTargetRestrictions(tgt);
|
||||
|
||||
@@ -20,7 +20,7 @@ Colors:blue
|
||||
Types:Legendary Planeswalker Jace
|
||||
Loyalty:5
|
||||
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumAtt$ -2 | IsCurse$ True | Duration$ UntilYourNextTurn | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Up to one target creature gets -2/-0 until your next turn.
|
||||
A:AB$ Effect | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | AILogic$ CastFromGraveThisTurn | ValidTgts$ Instant.YouCtrl,Sorcery.YouCtrl | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SubAbility$ DBEffect | SpellDescription$ You may cast target instant or sorcery card from your graveyard this turn. If that spell would be put into your graveyard this turn, exile it instead.
|
||||
A:AB$ Effect | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | AILogic$ ReplaySpell | ValidTgts$ Instant.YouCtrl,Sorcery.YouCtrl | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SubAbility$ DBEffect | SpellDescription$ You may cast target instant or sorcery card from your graveyard this turn. If that spell would be put into your graveyard this turn, exile it instead.
|
||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
||||
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Mission Briefing
|
||||
ManaCost:U U
|
||||
Types:Instant
|
||||
A:SP$ Surveil | Cost$ U U | Amount$ 2 | SubAbility$ DBChooseCard | SpellDescription$ Surveil 2, then choose an instant or sorcery card in your graveyard. You may cast it this turn. If that spell would be put into your graveyard this turn, exile it instead. (To surveil 2, look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)
|
||||
SVar:DBChooseCard:DB$ ChooseCard | Choices$ Instant.YouCtrl,Sorcery.YouCtrl | ChoiceZone$ Graveyard | AILogic$ CastFromGraveThisTurn | Mandatory$ True | RememberChosen$ True | SubAbility$ DBEffect | SpellDescription$ You may cast that card this turn. If that card would be put into your graveyard this turn, exile it instead.
|
||||
SVar:DBChooseCard:DB$ ChooseCard | Choices$ Instant.YouCtrl,Sorcery.YouCtrl | ChoiceZone$ Graveyard | Mandatory$ True | RememberChosen$ True | SubAbility$ DBEffect | SpellDescription$ You may cast that card this turn. If that card would be put into your graveyard this turn, exile it instead.
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ExileOnMoved$ Stack | ReplacementEffects$ ReplaceGraveyard
|
||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card.
|
||||
SVar:ReplaceGraveyard:Event$ Moved | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile | Description$ If that card would be put into your graveyard this turn, exile it instead.
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Artifact Equipment
|
||||
K:Equip:2
|
||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddTrigger$ AttackTrigger | Description$ Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice CARDNAME. If you do, create a token that's a copy of this creature."
|
||||
SVar:AttackTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature.
|
||||
SVar:TrigCopy:AB$ CopyPermanent | Cost$ Sac<1/OriginalHost/Trickster's Talisman> | Defined$ Self | NumCopies$ 1 | AILogic$ DuplicatePerms
|
||||
SVar:TrigCopy:AB$ CopyPermanent | Cost$ Sac<1/OriginalHost/Trickster's Talisman> | Defined$ Self | NumCopies$ 1
|
||||
DeckHas:Ability$Token
|
||||
Oracle:Invoke Duplicity — Equipped creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may sacrifice Trickster's Talisman. If you do, create a token that's a copy of this creature."\nEquip {2}
|
||||
|
||||
@@ -32,7 +32,7 @@ Strixhaven Stadium|The Mage Tower|And that's game, set, and match!
|
||||
Test of Endurance|The Test|So... did I pass?
|
||||
Thassa's Oracle|The Prophecy of Victory|I see... nothing. We must've won.
|
||||
The Cheese Stands Alone|The Cheese|It's cheesy, but hey, it works!
|
||||
The Deck of Many Things|Down on the Deck|Lucky draw!
|
||||
The Deck of Many Things's Effect|Down on the Deck|Lucky draw!
|
||||
Triskaidekaphobia|The Fear of 13|It's just a silly ancient superstition... right?
|
||||
Vorpal Sword|Snicker-Snack!|He left it dead, and with its head / He went galumphing back.
|
||||
Emblem - Vraska, Golgari Queen|The Flurry of Assassins|How good is your dodging?
|
||||
|
||||
@@ -697,9 +697,9 @@ public enum ColumnDef {
|
||||
return !(((IPaperCard) i).getRules().getType().isArtifact() && (toColor(i).isColorless() ||
|
||||
//If it isn't colorless, see if it can be paid with only white, only blue, only black.
|
||||
//No need to check others since three-color hybrid shards don't exist.
|
||||
manaCost.canBePaidWithAvaliable(MagicColor.WHITE) &&
|
||||
manaCost.canBePaidWithAvaliable(MagicColor.BLUE) &&
|
||||
manaCost.canBePaidWithAvaliable(MagicColor.BLACK)))
|
||||
manaCost.canBePaidWithAvailable(MagicColor.WHITE) &&
|
||||
manaCost.canBePaidWithAvailable(MagicColor.BLUE) &&
|
||||
manaCost.canBePaidWithAvailable(MagicColor.BLACK)))
|
||||
? "0" + toSplitLast(i) : "1";
|
||||
}
|
||||
|
||||
@@ -757,9 +757,9 @@ public enum ColumnDef {
|
||||
private static String toGoldFirst(final InventoryItem i) {
|
||||
forge.card.mana.ManaCost manaCost = ((IPaperCard) i).getRules().getManaCost();
|
||||
|
||||
return !(manaCost.canBePaidWithAvaliable(MagicColor.WHITE) | manaCost.canBePaidWithAvaliable(MagicColor.BLUE) |
|
||||
manaCost.canBePaidWithAvaliable(MagicColor.BLACK) | manaCost.canBePaidWithAvaliable(MagicColor.RED) |
|
||||
manaCost.canBePaidWithAvaliable(MagicColor.GREEN)) ? "0" : "1";
|
||||
return !(manaCost.canBePaidWithAvailable(MagicColor.WHITE) | manaCost.canBePaidWithAvailable(MagicColor.BLUE) |
|
||||
manaCost.canBePaidWithAvailable(MagicColor.BLACK) | manaCost.canBePaidWithAvailable(MagicColor.RED) |
|
||||
manaCost.canBePaidWithAvailable(MagicColor.GREEN)) ? "0" : "1";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,12 +90,12 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
|
||||
filename = filename0;
|
||||
isLimitedFormat = isLimitedFormat0;
|
||||
path = path0;
|
||||
addSharedAchivements();
|
||||
addSharedAchievements();
|
||||
addAchievements();
|
||||
load();
|
||||
}
|
||||
|
||||
protected void addSharedAchivements() {
|
||||
protected void addSharedAchievements() {
|
||||
add(new GameWinStreak(10, 25, 50, 100));
|
||||
add(new MatchWinStreak(10, 25, 50, 100));
|
||||
add(new TotalGameWins(250, 500, 1000, 2000));
|
||||
|
||||
@@ -18,7 +18,7 @@ public class AltWinAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSharedAchivements() {
|
||||
protected void addSharedAchievements() {
|
||||
//prevent including shared achievements
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ChallengeAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSharedAchivements() {
|
||||
protected void addSharedAchievements() {
|
||||
//prevent including shared achievements
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public class PlaneswalkerAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSharedAchivements() {
|
||||
protected void addSharedAchievements() {
|
||||
//prevent including shared achievements
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ public class PuzzleAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSharedAchivements() {
|
||||
protected void addSharedAchievements() {
|
||||
//prevent including shared achievements
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user