mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 01:38:13 +00:00
C21 Veyran & Panharmonicon rework
This commit is contained in:
@@ -337,17 +337,17 @@ public class ComputerUtilCost {
|
||||
}
|
||||
|
||||
public static boolean isSacrificeSelfCost(final Cost cost) {
|
||||
if (cost == null) {
|
||||
return false;
|
||||
}
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostSacrifice) {
|
||||
if ("CARDNAME".equals(part.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (cost == null) {
|
||||
return false;
|
||||
}
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostSacrifice) {
|
||||
if ("CARDNAME".equals(part.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,13 +367,13 @@ public class ComputerUtilCost {
|
||||
if (part instanceof CostTapType) {
|
||||
/*
|
||||
* Only crew with creatures weaker than vehicle
|
||||
*
|
||||
*
|
||||
* Possible improvements:
|
||||
* - block against evasive (flyers, intimidate, etc.)
|
||||
* - break board stall by racing with evasive vehicle
|
||||
*/
|
||||
if (sa.hasParam("Crew")) {
|
||||
Card vehicle = AnimateAi.becomeAnimated(source, sa);
|
||||
Card vehicle = AnimateAi.becomeAnimated(source, sa);
|
||||
final int vehicleValue = ComputerUtilCard.evaluateCreature(vehicle);
|
||||
String type = part.getType();
|
||||
String totalP = type.split("withTotalPowerGE")[1];
|
||||
@@ -390,7 +390,7 @@ public class ComputerUtilCost {
|
||||
return ComputerUtil.chooseTapTypeAccumulatePower(ai, type, sa, true,
|
||||
Integer.parseInt(totalP), exclude) != null;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -478,9 +478,9 @@ public class ComputerUtilCost {
|
||||
}
|
||||
}
|
||||
for (Card c : player.getCardsIn(ZoneType.Command)) {
|
||||
if (cannotBeCountered) {
|
||||
continue;
|
||||
}
|
||||
if (cannotBeCountered) {
|
||||
continue;
|
||||
}
|
||||
final String snem = c.getSVar("SpellsNeedExtraManaEffect");
|
||||
if (!StringUtils.isBlank(snem)) {
|
||||
if (StringUtils.isNumeric(snem)) {
|
||||
@@ -548,7 +548,7 @@ public class ComputerUtilCost {
|
||||
}
|
||||
|
||||
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded)
|
||||
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
} // canPayCost()
|
||||
|
||||
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
|
||||
@@ -609,10 +609,10 @@ public class ComputerUtilCost {
|
||||
return false;
|
||||
}
|
||||
} else if (aiLogic != null && aiLogic.startsWith("LifeLE")) {
|
||||
// if payer can't lose life its no need to pay unless
|
||||
if (!payer.canLoseLife())
|
||||
return false;
|
||||
else if (payer.getLife() <= Integer.valueOf(aiLogic.substring(6))) {
|
||||
// if payer can't lose life its no need to pay unless
|
||||
if (!payer.canLoseLife())
|
||||
return false;
|
||||
else if (payer.getLife() <= AbilityUtils.calculateAmount(source, aiLogic.substring(6), sa)) {
|
||||
return true;
|
||||
}
|
||||
} else if ("WillAttack".equals(aiLogic)) {
|
||||
@@ -638,13 +638,13 @@ public class ComputerUtilCost {
|
||||
// Didn't have any of the data on the original SA to pay dependant costs
|
||||
|
||||
return checkLifeCost(payer, cost, source, 4, sa)
|
||||
&& checkDamageCost(payer, cost, source, 4)
|
||||
&& (isMine || checkSacrificeCost(payer, cost, source, sa))
|
||||
&& (isMine || checkDiscardCost(payer, cost, source, sa))
|
||||
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
|
||||
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
|
||||
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
|
||||
&& (!source.getName().equals("Chain of Vapor") || (payer.getWeakestOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
|
||||
&& checkDamageCost(payer, cost, source, 4)
|
||||
&& (isMine || checkSacrificeCost(payer, cost, source, sa))
|
||||
&& (isMine || checkDiscardCost(payer, cost, source, sa))
|
||||
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
|
||||
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
|
||||
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
|
||||
&& (!source.getName().equals("Chain of Vapor") || (payer.getWeakestOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
|
||||
}
|
||||
|
||||
public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) {
|
||||
|
||||
@@ -1898,8 +1898,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyword.startsWith("CantBeCounteredBy") || keyword.startsWith("Panharmonicon")
|
||||
|| keyword.startsWith("Dieharmonicon") || keyword.startsWith("Shrineharmonicon")) {
|
||||
if (keyword.startsWith("CantBeCounteredBy")) {
|
||||
final String[] p = keyword.split(":");
|
||||
sbLong.append(p[2]).append("\r\n");
|
||||
} else if (keyword.startsWith("etbCounter")) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.GameStage;
|
||||
import forge.game.IIdentifiable;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
@@ -45,6 +46,7 @@ import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.CardTranslation;
|
||||
@@ -458,6 +460,19 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean applyAbility(final String mode, final Trigger trigger, final Map<AbilityKey, Object> runParams) {
|
||||
// don't apply the ability if it hasn't got the right mode
|
||||
if (!getParam("Mode").equals(mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode.equals("Panharmonicon")) {
|
||||
return StaticAbilityPanharmonicon.applyPanharmoniconAbility(this, trigger, runParams);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Cost getAttackCost(final Card attacker, final GameEntity target) {
|
||||
if (this.isSuppressed() || !getParam("Mode").equals("CantAttackUnless") || !this.checkConditions()) {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StaticAbilityPanharmonicon {
|
||||
public static boolean applyPanharmoniconAbility(final StaticAbility stAb, final Trigger trigger, final Map<AbilityKey, Object> runParams) {
|
||||
final Card card = stAb.getHostCard();
|
||||
final Game game = card.getGame();
|
||||
|
||||
final Card trigHost = trigger.getHostCard();
|
||||
final TriggerType trigMode = trigger.getMode();
|
||||
|
||||
// What card is the source of the trigger?
|
||||
if (!stAb.matchesValidParam("ValidCard", trigHost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is our trigger's mode among the other modes?
|
||||
if (stAb.hasParam("ValidMode")) {
|
||||
List<String> modes = new ArrayList<>(Arrays.asList(stAb.getParam("ValidMode").split(",")));
|
||||
if (!modes.contains(trigMode.toString())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (trigMode.equals(TriggerType.ChangesZone)) {
|
||||
// Cause of the trigger – the card changing zones
|
||||
final Card trigCause = (Card) runParams.get(AbilityKey.Card);
|
||||
if (stAb.hasParam("ValidCause")) {
|
||||
if (!trigCause.isValid(stAb.getParam("ValidCause").split(","),
|
||||
game.getPhaseHandler().getPlayerTurn(), trigHost, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (stAb.hasParam("Origin")) {
|
||||
final String origin = (String) runParams.get(AbilityKey.Origin);
|
||||
if (!origin.equals(stAb.getParam("Origin"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (stAb.hasParam("Destination")) {
|
||||
final String destination = (String) runParams.get(AbilityKey.Destination);
|
||||
if (!destination.equals(stAb.getParam("Destination"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (trigMode.equals(TriggerType.ChangesZoneAll)) {
|
||||
// Check if the cards have a trigger at all
|
||||
final String origin = stAb.hasParam("Origin") ? stAb.getParam("Origin") : null;
|
||||
final String destination = stAb.hasParam("Destination") ? stAb.getParam("Destination") : null;
|
||||
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||
|
||||
// If the origin isn't specified, it's null – not making a list out of that.
|
||||
if (origin == null) {
|
||||
if (table.filterCards(null, ZoneType.smartValueOf(destination), stAb.getParam("ValidCause"), trigHost, null).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (table.filterCards(ImmutableList.of(ZoneType.smartValueOf(origin)), ZoneType.smartValueOf(destination), stAb.getParam("ValidCause"), trigHost, null).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (trigMode.equals(TriggerType.SpellCastOrCopy)
|
||||
|| trigMode.equals(TriggerType.SpellCast) || trigMode.equals(TriggerType.SpellCopy)) {
|
||||
// Check if the spell cast and the caster match
|
||||
final SpellAbility sa = (SpellAbility) runParams.get(AbilityKey.CastSA);
|
||||
if (stAb.hasParam("ValidCause")) {
|
||||
if (!sa.getHostCard().isValid(stAb.getParam("ValidCause").split(","),
|
||||
game.getPhaseHandler().getPlayerTurn(), trigHost, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (stAb.hasParam("ValidActivator")) {
|
||||
if (!sa.getActivatingPlayer().isValid(stAb.getParam("ValidActivator").split(","),
|
||||
game.getPhaseHandler().getPlayerTurn(), trigHost, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -34,16 +34,12 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.effects.CharmEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.*;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.FileSection;
|
||||
@@ -362,7 +358,7 @@ public class TriggerHandler {
|
||||
|
||||
for (final Trigger t : triggers) {
|
||||
if (!t.isStatic() && t.getHostCard().getController().equals(player) && canRunTrigger(t, mode, runParams)) {
|
||||
int x = 1 + handlePanharmonicon(t, runParams, player);
|
||||
int x = 1 + handlePanharmonicon(t, runParams);
|
||||
|
||||
for (int i = 0; i < x; ++i) {
|
||||
runSingleTrigger(t, runParams);
|
||||
@@ -626,100 +622,14 @@ public class TriggerHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private int handlePanharmonicon(final Trigger t, final Map<AbilityKey, Object> runParams, final Player p) {
|
||||
Card host = t.getHostCard();
|
||||
private int handlePanharmonicon(final Trigger t, final Map<AbilityKey, Object> runParams) {
|
||||
int n = 0;
|
||||
|
||||
// Sanctum of All
|
||||
if (host.isShrine() && host.isInZone(ZoneType.Battlefield) && p.equals(host.getController())) {
|
||||
int shrineCount = CardLists.count(p.getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Shrine"));
|
||||
if (shrineCount >= 6) {
|
||||
for (final Card ck : p.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (final KeywordInterface ki : ck.getKeywords()) {
|
||||
final String kw = ki.getOriginal();
|
||||
if (kw.startsWith("Shrineharmonicon")) {
|
||||
final String valid = kw.split(":")[1];
|
||||
if (host.isValid(valid.split(","), p, ck, null)) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not a changesZone trigger or changesZoneAll
|
||||
if (t.getMode() != TriggerType.ChangesZone && t.getMode() != TriggerType.ChangesZoneAll) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// leave battlefield trigger, might be dying
|
||||
// only real changeszone look back for this
|
||||
if (t.getMode() == TriggerType.ChangesZone && "Battlefield".equals(t.getParam("Origin"))) {
|
||||
// Need to get the last info from the trigger host
|
||||
host = game.getChangeZoneLKIInfo(host);
|
||||
}
|
||||
|
||||
// not a Permanent you control
|
||||
if (!host.isPermanent() || !host.isInZone(ZoneType.Battlefield)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (t.getMode() == TriggerType.ChangesZone) {
|
||||
// iterate over all cards
|
||||
for (final Card ck : CardLists.filterControlledBy(p.getGame().getLastStateBattlefield(), p)) {
|
||||
for (final KeywordInterface ki : ck.getKeywords()) {
|
||||
final String kw = ki.getOriginal();
|
||||
if (kw.startsWith("Panharmonicon")) {
|
||||
// Enter the Battlefield Trigger
|
||||
if (runParams.get(AbilityKey.Destination) instanceof String) {
|
||||
final String dest = (String) runParams.get(AbilityKey.Destination);
|
||||
if ("Battlefield".equals(dest) && runParams.get(AbilityKey.Card) instanceof Card) {
|
||||
final Card card = (Card) runParams.get(AbilityKey.Card);
|
||||
final String valid = kw.split(":")[1];
|
||||
if (card.isValid(valid.split(","), p, ck, null)) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (kw.startsWith("Dieharmonicon")) {
|
||||
// 700.4. The term dies means "is put into a graveyard from the battlefield."
|
||||
if (runParams.get(AbilityKey.Origin) instanceof String) {
|
||||
final String origin = (String) runParams.get(AbilityKey.Origin);
|
||||
if ("Battlefield".equals(origin) && runParams.get(AbilityKey.Destination) instanceof String) {
|
||||
final String dest = (String) runParams.get(AbilityKey.Destination);
|
||||
if ("Graveyard".equals(dest) && runParams.get(AbilityKey.Card) instanceof Card) {
|
||||
final Card card = (Card) runParams.get(AbilityKey.Card);
|
||||
final String valid = kw.split(":")[1];
|
||||
if (card.isValid(valid.split(","), p, ck, null)) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (t.getMode() == TriggerType.ChangesZoneAll) {
|
||||
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
|
||||
// iterate over all cards that are on the battlefield right now, don't use last state
|
||||
for (final Card ck : p.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (final KeywordInterface ki : ck.getKeywords()) {
|
||||
final String kw = ki.getOriginal();
|
||||
if (kw.startsWith("Panharmonicon")) {
|
||||
// currently there is no ChangesZoneAll that would trigger on etb
|
||||
final String valid = kw.split(":")[1];
|
||||
if (!table.filterCards(null, ZoneType.Battlefield, valid, ck, null).isEmpty()) {
|
||||
n++;
|
||||
}
|
||||
} else if (kw.startsWith("Dieharmonicon")) {
|
||||
// 700.4. The term dies means "is put into a graveyard from the battlefield."
|
||||
final String valid = kw.split(":")[1];
|
||||
if (!table.filterCards(ImmutableList.of(ZoneType.Battlefield), ZoneType.Graveyard,
|
||||
valid, ck, null).isEmpty()) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
// Checks only the battlefield, as those effects only work from there
|
||||
for (final Card ca : game.getLastStateBattlefield()) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (stAb.applyAbility("Panharmonicon", t, runParams) && stAb.checkConditions()) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ Types:Creature Elemental
|
||||
PT:5/7
|
||||
K:Reach
|
||||
S:Mode$ Continuous | Affected$ Land.YouCtrl | MayPlay$ True | AffectedZone$ Graveyard | Description$ You may play lands from your graveyard.
|
||||
K:Panharmonicon:Land:If a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
S:Mode$ Panharmonicon | ValidMode$ ChangesZone,ChangesZoneAll | ValidCard$ Permanent.YouCtrl | ValidCause$ Land | Destination$ Battlefield | Description$ If a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
Oracle:Reach\nYou may play lands from your graveyard.\nIf a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# TODO: Improve AI logic. Currently AI will sacrifice even if Troll can't attack at all.
|
||||
Name:Clackbridge Troll
|
||||
ManaCost:3 B B
|
||||
Types:Creature Troll
|
||||
|
||||
@@ -2,7 +2,6 @@ Name:Naban, Dean of Iteration
|
||||
ManaCost:1 U
|
||||
Types:Legendary Creature Human Wizard
|
||||
PT:2/1
|
||||
K:Panharmonicon:Wizard.YouCtrl:If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
S:Mode$ Panharmonicon | ValidMode$ ChangesZone,ChangesZoneAll | ValidCard$ Permanent.YouCtrl | ValidCause$ Wizard.YouCtrl | Destination$ Battlefield | Description$ If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
DeckHints:Type$Wizard
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/naban_dean_of_iteration.jpg
|
||||
Oracle:If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
Name:Panharmonicon
|
||||
ManaCost:4
|
||||
Types:Artifact
|
||||
K:Panharmonicon:Creature,Artifact:If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/panharmonicon.jpg
|
||||
S:Mode$ Panharmonicon | ValidMode$ ChangesZone,ChangesZoneAll | ValidCard$ Permanent.YouCtrl | ValidCause$ Artifact,Creature | Destination$ Battlefield | Description$ If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
Oracle:If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
|
||||
@@ -3,6 +3,6 @@ ManaCost:W U B R G
|
||||
Types:Legendary Enchantment Shrine
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigSearch | TriggerDescription$ At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.
|
||||
SVar:TrigSearch:DB$ ChangeZone | Origin$ Library | OriginChoice$ True | OriginAlternative$ Graveyard | AlternativeMessage$ Would you like to search your library with this ability? If you do, your library will be shuffled. | Destination$ Battlefield | ChangeType$ Card.Shrine
|
||||
K:Shrineharmonicon:Shrine.Other+YouCtrl:If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.
|
||||
S:Mode$ Panharmonicon | ValidCard$ Shrine.Other+YouCtrl | IsPresent$ Shrine.YouCtrl | PresentCompare$ GE6 | Description$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.
|
||||
DeckHints:Type$Shrine
|
||||
Oracle:At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.\nIf an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Teysa Karlov
|
||||
ManaCost:2 W B
|
||||
Types:Legendary Creature Human Advisor
|
||||
PT:2/4
|
||||
K:Dieharmonicon:Creature:If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
S:Mode$ Panharmonicon | ValidMode$ ChangesZone,ChangesZoneAll | ValidCard$ Permanent.YouCtrl | ValidCause$ Creature | Origin$ Battlefield | Destination$ Graveyard | Description$ If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
S:Mode$ Continuous | Affected$ Creature.token+YouCtrl | AddKeyword$ Vigilance & Lifelink | Description$ Creature tokens you control have vigilance and lifelink.
|
||||
DeckHints:Ability$Token
|
||||
Oracle:If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.\nCreature tokens you control have vigilance and lifelink.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Name:Veyran, Voice of Duality
|
||||
ManaCost:1 U R
|
||||
Types:Legendary Creature Efreet Wizard
|
||||
PT:2/2
|
||||
T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Magecraft - Whenever you cast or copy an instant or sorcery spell, CARDNAME gets +1/+1 until end of turn.
|
||||
SVar:TrigPump:DB$ Pump | NumAtt$ 1 | NumDef$ 1
|
||||
S:Mode$ Panharmonicon | ValidMode$ SpellCast,SpellCopy,SpellCastOrCopy | ValidCard$ Permanent.YouCtrl | ValidCause$ Instant,Sorcery | ValidActivator$ You | Description$ If you casting or copying an instant or sorcery spell causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
SVar:BuffedBy:Instant,Sorcery
|
||||
DeckNeeds:Type$Instant|Sorcery
|
||||
Oracle:Magecraft — Whenever you cast or copy an instant or sorcery spell, Veyran, Voice of Duality gets +1/+1 until end of turn.\nIf you casting or copying an instant or sorcery spell causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
@@ -4,5 +4,5 @@ Types:Legendary Creature Elemental Horror
|
||||
PT:3/5
|
||||
K:Deathtouch
|
||||
K:Lifelink
|
||||
K:Panharmonicon:Permanent:If a permanent entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
S:Mode$ Panharmonicon | ValidMode$ ChangesZone,ChangesZoneAll | ValidCard$ Permanent | ValidCause$ Wizard.YouCtrl | Destination$ Battlefield | Description$ If a permanent entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
Oracle:Deathtouch, lifelink\nIf a permanent entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
|
||||
Reference in New Issue
Block a user