mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Fix X shards in combat tax missing from total cost (#5154)
This commit is contained in:
@@ -11,6 +11,7 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
@@ -30,6 +31,7 @@ import forge.util.MyRandom;
|
||||
import forge.util.TextUtil;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -68,6 +70,41 @@ public class EffectAi extends SpellAbilityAi {
|
||||
}
|
||||
randomReturn = true;
|
||||
}
|
||||
} else if (logic.equals("RestrictBlocking")) {
|
||||
if (!phase.isPlayerTurn(ai) || phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)
|
||||
|| phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sa.getPayCosts().getTotalMana().countX() > 0 && sa.getHostCard().getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to half the remaining mana to allow for Main 2 and other combat shenanigans.
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai, sa.isTrigger()) / 2;
|
||||
if (xPay == 0) { return false; }
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
Player opp = ai.getStrongestOpponent();
|
||||
List<Card> possibleAttackers = ai.getCreaturesInPlay();
|
||||
List<Card> possibleBlockers = opp.getCreaturesInPlay();
|
||||
possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED);
|
||||
final Combat combat = game.getCombat();
|
||||
int oppLife = opp.getLife();
|
||||
int potentialDmg = 0;
|
||||
List<Card> currentAttackers = new ArrayList<>();
|
||||
|
||||
if (possibleBlockers.isEmpty()) { return false; }
|
||||
|
||||
for (final Card creat : possibleAttackers) {
|
||||
if (CombatUtil.canAttack(creat, opp) && possibleBlockers.size() > 1) {
|
||||
potentialDmg += creat.getCurrentPower();
|
||||
if (potentialDmg >= oppLife) { return true; }
|
||||
}
|
||||
if (combat != null && combat.isAttacking(creat)) {
|
||||
currentAttackers.add(creat);
|
||||
}
|
||||
}
|
||||
|
||||
return currentAttackers.size() > possibleBlockers.size();
|
||||
} else if (logic.equals("Fog")) {
|
||||
FogAi fogAi = new FogAi();
|
||||
if (!fogAi.canPlayAI(ai, sa)) {
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.WrappedAbility;
|
||||
@@ -21,51 +9,6 @@ public class StoreSVarAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = ai.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final Player opp = ai.getOpponents().get(0);
|
||||
|
||||
if (sa.hasParam("AILogic")) {
|
||||
if (sa.getPayCosts().getTotalMana().countX() > 0 && source.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to half the remaining mana to allow for Main 2 and other combat shenanigans.
|
||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai, sa.isTrigger()) / 2;
|
||||
if (xPay == 0) { return false; }
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
final String logic = sa.getParam("AILogic");
|
||||
if (logic.equals("RestrictBlocking")) {
|
||||
if (!ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_BEGIN)
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Card> possibleAttackers = ai.getCreaturesInPlay();
|
||||
List<Card> possibleBlockers = opp.getCreaturesInPlay();
|
||||
possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED);
|
||||
int oppLife = opp.getLife();
|
||||
int potentialDmg = 0;
|
||||
List<Card> currentAttackers = new ArrayList<>();
|
||||
|
||||
if (possibleBlockers.size() == 0) { return false; }
|
||||
|
||||
for (final Card creat : possibleAttackers) {
|
||||
if (CombatUtil.canAttack(creat, opp) && possibleBlockers.size() > 1) {
|
||||
potentialDmg += creat.getCurrentPower();
|
||||
if (potentialDmg >= oppLife) { return true; }
|
||||
}
|
||||
if (combat != null && combat.isAttacking(creat)) {
|
||||
currentAttackers.add(creat);
|
||||
}
|
||||
}
|
||||
|
||||
return currentAttackers.size() > possibleBlockers.size();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,10 @@ public class CombatUtil {
|
||||
// If there's a better way of handling this somewhere deeper in the code, feel free to remove
|
||||
final SpellAbility fakeSA = new SpellAbility.EmptySa(attacker, attacker.getController());
|
||||
fakeSA.setCardState(attacker.getCurrentState());
|
||||
// need to set this for "CostContainsX" restriction
|
||||
fakeSA.setPayCosts(attackCost);
|
||||
// prevent recalculating X
|
||||
fakeSA.setSVar("X", "0");
|
||||
return attacker.getController().getController().payManaOptional(attacker, attackCost, fakeSA,
|
||||
"Pay additional cost to declare " + attacker + " an attacker", ManaPaymentPurpose.DeclareAttacker);
|
||||
}
|
||||
@@ -347,6 +351,8 @@ public class CombatUtil {
|
||||
|
||||
SpellAbility fakeSA = new SpellAbility.EmptySa(blocker, blocker.getController());
|
||||
fakeSA.setCardState(blocker.getCurrentState());
|
||||
fakeSA.setPayCosts(blockCost);
|
||||
fakeSA.setSVar("X", "0");
|
||||
return blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class CostPartMana extends CostPart {
|
||||
if (isCostPayAnyNumberOfTimes) {
|
||||
int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa);
|
||||
if (timesToPay == 0) {
|
||||
return ManaCost.NO_COST;
|
||||
return ManaCost.ZERO;
|
||||
}
|
||||
ManaCostBeingPaid totalMana = new ManaCostBeingPaid(getMana());
|
||||
for (int i = 1; i < timesToPay; i++) {
|
||||
|
||||
@@ -254,7 +254,12 @@ public class StaticAbilityCantAttackBlock {
|
||||
if (remember) {
|
||||
hostCard.addRemembered(attacker);
|
||||
}
|
||||
// keep X shards
|
||||
boolean addX = costString.startsWith("X");
|
||||
costString = Integer.toString(AbilityUtils.calculateAmount(hostCard, stAb.getSVar(costString), stAb));
|
||||
if (addX) {
|
||||
costString += " X";
|
||||
}
|
||||
if (remember) {
|
||||
hostCard.removeRemembered(attacker);
|
||||
}
|
||||
@@ -288,7 +293,11 @@ public class StaticAbilityCantAttackBlock {
|
||||
}
|
||||
String costString = stAb.getParam("Cost");
|
||||
if (stAb.hasSVar(costString)) {
|
||||
costString = Integer.toString(AbilityUtils.calculateAmount(hostCard, costString, stAb));
|
||||
boolean addX = costString.startsWith("X");
|
||||
costString = Integer.toString(AbilityUtils.calculateAmount(hostCard, stAb.getSVar(costString), stAb));
|
||||
if (addX) {
|
||||
costString += " X";
|
||||
}
|
||||
}
|
||||
|
||||
return new Cost(costString, true);
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:W
|
||||
Types:Enchantment Aura
|
||||
K:Enchant creature
|
||||
A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Curse
|
||||
S:Mode$ CantAttackUnless | ValidCard$ Creature.AttachedBy | Cost$ X | Description$ Enchanted creature can't attack or block unless its controller pays {1} for each card in your hand.
|
||||
S:Mode$ CantBlockUnless | ValidCard$ Creature.AttachedBy | Cost$ X
|
||||
SVar:X:Count$InYourHand
|
||||
S:Mode$ CantAttackUnless | ValidCard$ Creature.AttachedBy | Cost$ Y | Description$ Enchanted creature can't attack or block unless its controller pays {1} for each card in your hand.
|
||||
S:Mode$ CantBlockUnless | ValidCard$ Creature.AttachedBy | Cost$ Y
|
||||
SVar:Y:Count$InYourHand
|
||||
Oracle:Enchant creature\nEnchanted creature can't attack or block unless its controller pays {1} for each card in your hand.
|
||||
|
||||
@@ -6,7 +6,7 @@ T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription
|
||||
SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | SubAbility$ DBInvestigate
|
||||
SVar:DBInvestigate:DB$ Investigate
|
||||
T:Mode$ Sacrificed | ValidPlayer$ You | ValidCard$ Clue.YouCtrl | Execute$ TrigClone | TriggerZones$ Battlefield | TriggerDescription$ Whenever you sacrifice a Clue, you may have NICKNAME become a copy of a creature card exiled with it until end of turn.
|
||||
SVar:TrigClone:DB$ Clone | Choices$ Creature.ExiledWithSource | ChoiceZone$ Exile | Optional$ True
|
||||
SVar:TrigClone:DB$ Clone | Choices$ Creature.ExiledWithSource | ChoiceZone$ Exile | Optional$ True | Duration$ UntilEndOfTurn
|
||||
SVar:HasAttackEffect:TRUE
|
||||
DeckHas:Ability$Investigate|Token|Graveyard & Type$Artifact|Clue
|
||||
DeckHints:Ability$Graveyard
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:2 R
|
||||
Types:Creature Human Rogue
|
||||
PT:3/2
|
||||
K:Trample
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | IsPresent$ Card.Outlaw+StrictlyOther | TriggerZones$ Battlefield | Execute$ TrigTreasure | TriggerDescription$ When CARDNAME enters the battlefield, if you control another outlaw, create a Treasure token. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws. A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.")
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | IsPresent$ Card.Outlaw+StrictlyOther+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigTreasure | TriggerDescription$ When CARDNAME enters the battlefield, if you control another outlaw, create a Treasure token. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws. A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.")
|
||||
SVar:TrigTreasure:DB$ Token | TokenScript$ c_a_treasure_sac
|
||||
DeckHas:Ability$Token|Sacrifice & Type$Artifact|Treasure
|
||||
Oracle:Trample\nWhen Mine Raider enters the battlefield, if you control another outlaw, create a Treasure token. (Assassins, Mercenaries, Pirates, Rogues, and Warlocks are outlaws. A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.")
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Artifact Creature Myr
|
||||
PT:2/2
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your upkeep, put a +1/+1 counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
|
||||
S:Mode$ CantAttackUnless | ValidCard$ Card.Self | Cost$ X | Description$ CARDNAME can't attack or block unless you pay {1} for each +1/+1 counter on it.
|
||||
S:Mode$ CantBlockUnless | ValidCard$ Card.Self | Cost$ X
|
||||
SVar:X:Count$CardCounters.P1P1
|
||||
S:Mode$ CantAttackUnless | ValidCard$ Card.Self | Cost$ Y | Description$ CARDNAME can't attack or block unless you pay {1} for each +1/+1 counter on it.
|
||||
S:Mode$ CantBlockUnless | ValidCard$ Card.Self | Cost$ Y
|
||||
SVar:Y:Count$CardCounters.P1P1
|
||||
Oracle:At the beginning of your upkeep, put a +1/+1 counter on Myr Prototype.\nMyr Prototype can't attack or block unless you pay {1} for each +1/+1 counter on it.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 B B
|
||||
Types:Sorcery
|
||||
K:Spree
|
||||
A:SP$ Charm | Choices$ DBSacrifice,DBDiscard,DBLoseLife | MinCharmNum$ 1 | CharmNum$ 3 | Spree$ True
|
||||
SVar:DBSacrifice:DB$ Sacrifice | SpreeCost$ 1 | ValidTgts$ Opponent | Amount$ Count$Valid Creature.ThisTargetedPlayerCtrl/HalfUp | SacValid$ Creature | Mode$ TgtChoose | SpellDescription$ Target opponent sacrifices half the creatures they control, rounded up.
|
||||
SVar:DBSacrifice:DB$ Sacrifice | SpreeCost$ 1 | ValidTgts$ Opponent | Amount$ ThisTargetedPlayer$Valid Creature.YouCtrl/HalfUp | SacValid$ Creature | Mode$ TgtChoose | SpellDescription$ Target opponent sacrifices half the creatures they control, rounded up.
|
||||
SVar:DBDiscard:DB$ Discard | SpreeCost$ 1 | ValidTgts$ Opponent | NumCards$ ThisTargetedPlayer$CardsInHand/HalfUp | Mode$ TgtChoose | SpellDescription$ Target opponent discards half the cards in their hand, rounded up.
|
||||
SVar:DBLoseLife:DB$ LoseLife | SpreeCost$ 2 | ValidTgts$ Opponent | LifeAmount$ ThisTargetedPlayer$LifeTotal/HalfUp | SpellDescription$ Target opponent loses half their life, rounded up.
|
||||
Oracle:Spree (Choose one or more additional costs.)\n+ {1} — Target opponent sacrifices half the creatures they control, rounded up.\n+ {2} — Target opponent discards half the cards in their hand, rounded up.\n+ {2} — Target opponent loses half their life, rounded up.
|
||||
|
||||
@@ -3,9 +3,9 @@ ManaCost:2 B
|
||||
Types:Creature Skeleton
|
||||
PT:0/0
|
||||
K:etbCounter:P1P1:1
|
||||
A:AB$ Regenerate | Cost$ X | CostDesc$ Pay {1} for each +1/+1 counter on CARDNAME: | RegenerationAbility$ DBImmediateTrigger | StackDescription$ SpellDescription | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a +1/+1 counter on it.
|
||||
A:AB$ Regenerate | Cost$ Mana<1\NumTimes> | CostDesc$ Pay {1} for each +1/+1 counter on CARDNAME: | RegenerationAbility$ DBImmediateTrigger | StackDescription$ SpellDescription | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a +1/+1 counter on it.
|
||||
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigPutCounter | TriggerDescription$ When it regenerates this way, put a +1/+1 counter on it.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ EffectSource | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a +1/+1 counter on it.
|
||||
SVar:X:Count$CardCounters.P1P1
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ EffectSource | CounterType$ P1P1 | CounterNum$ 1
|
||||
SVar:NumTimes:Count$CardCounters.P1P1
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Skeleton Scavengers enters the battlefield with a +1/+1 counter on it.\nPay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Snakeskin Veil
|
||||
ManaCost:G
|
||||
Types:Instant
|
||||
A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature.
|
||||
A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature you control.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Hexproof
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Put a +1/+1 counter on target creature you control. It gains hexproof until end of turn.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
Name:War Cadence
|
||||
ManaCost:2 R
|
||||
Types:Enchantment
|
||||
A:AB$ StoreSVar | Cost$ X R | SVar$ PaidNum | Type$ Count | Expression$ xPaid | SubAbility$ CadenceEffect | AILogic$ RestrictBlocking | SpellDescription$ This turn, creatures can't block unless their controller pays {X} for each blocking creature they control.
|
||||
SVar:CadenceEffect:DB$ Effect | StaticAbilities$ CadenceStaticAb | Stackable$ False | RememberObjects$ Valid Creature.blocking
|
||||
SVar:CadenceStaticAb:Mode$ CantBlockUnless | ValidCard$ Card.IsNotRemembered | Cost$ PaidNum | EffectZone$ Command | Description$ This turn, creatures can't block unless their controller pays {X} for each blocking creature they control.
|
||||
A:AB$ Effect | Cost$ X R | StaticAbilities$ CadenceStaticAb | Stackable$ False | SetChosenNumber$ X | AILogic$ RestrictBlocking | SpellDescription$ This turn, creatures can't block unless their controller pays {X} for each blocking creature they control.
|
||||
SVar:CadenceStaticAb:Mode$ CantBlockUnless | Cost$ XChosen | EffectZone$ Command | Description$ This turn, creatures can't block unless their controller pays {X} for each blocking creature they control.
|
||||
SVar:X:Count$xPaid
|
||||
SVar:PaidNum:Number$0
|
||||
SVar:XChosen:Count$ChosenNumber
|
||||
SVar:NonStackingEffect:True
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{X}{R}: This turn, creatures can't block unless their controller pays {X} for each blocking creature they control.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
Name:War Tax
|
||||
ManaCost:2 U
|
||||
Types:Enchantment
|
||||
A:AB$ StoreSVar | SVar$ Y | Type$ Count | Expression$ xPaid | Cost$ X U | SubAbility$ DBEffect | SpellDescription$ This turn, creatures can't attack unless their controller pays {X} for each attacking creature they control.
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ AttackTax | SubAbility$ DBReset | EffectOwner$ SourceController
|
||||
SVar:AttackTax:Mode$ CantAttackUnless | ValidCard$ Creature | EffectZone$ Command | Cost$ Y | Description$ Creatures can't attack unless their controller pays {X} for each attacking creature they control.
|
||||
SVar:DBReset:DB$ StoreSVar | SVar$ Y | Type$ Number | Expression$ 0
|
||||
A:AB$ Effect | Cost$ X U | StaticAbilities$ AttackTax | SetChosenNumber$ X | SpellDescription$ This turn, creatures can't attack unless their controller pays {X} for each attacking creature they control.
|
||||
SVar:AttackTax:Mode$ CantAttackUnless | ValidCard$ Creature | EffectZone$ Command | Cost$ XChosen | Description$ Creatures can't attack unless their controller pays {X} for each attacking creature they control.
|
||||
SVar:X:Count$xPaid
|
||||
SVar:Y:Number$0
|
||||
SVar:XChosen:Count$ChosenNumber
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{X}{U}: This turn, creatures can't attack unless their controller pays {X} for each attacking creature they control.
|
||||
|
||||
@@ -7,6 +7,6 @@ SVar:LoseGain:DB$ LoseLife | ValidTgts$ Opponent | LifeAmount$ 2 | SubAbility$ D
|
||||
SVar:DBGain:DB$ GainLife | Defined$ You | LifeAmount$ 2
|
||||
A:AB$ Repeat | Cost$ T Sac<1/Creature.Other/another creature> | RepeatSubAbility$ DBDraw | MaxRepeat$ 2 | RepeatPresent$ Land.YouCtrl | RepeatCompare$ GE8 | StackDescription$ SpellDescription | SpellDescription$ Draw a card. You may put a land card from your hand onto the battlefield tapped. If you control eight or more lands, repeat this process once.
|
||||
SVar:DBDraw:DB$ Draw | SubAbility$ DBChangeZone
|
||||
DeckHas:Ability$LifeGain|Sacrifice
|
||||
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | Optional$ You | ChangeType$ Land | ChangeNum$ 1 | Tapped$ True
|
||||
DeckHas:Ability$LifeGain|Sacrifice
|
||||
Oracle:Whenever you draw your second card each turn, target opponent loses 2 life and you gain 2 life.\n{T}, Sacrifice another creature: Draw a card. You may put a land card from your hand onto the battlefield tapped. If you control eight or more lands, repeat this process once.
|
||||
|
||||
Reference in New Issue
Block a user