From feefe6204799527b81c9ea98dba6214fd4047264 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Wed, 4 Nov 2020 20:43:40 +0100 Subject: [PATCH] SpellAbilityEffect: unify Attacking options --- .../game/ability/SpellAbilityEffect.java | 60 ++++++++++ .../ability/effects/ChangeZoneEffect.java | 108 +++--------------- .../ability/effects/ControlGainEffect.java | 31 ++--- .../forge/game/ability/effects/DigEffect.java | 40 ++----- .../game/ability/effects/DigUntilEffect.java | 30 ++--- .../game/ability/effects/TokenEffectBase.java | 60 +--------- .../res/cardsfolder/k/kaalia_of_the_vast.txt | 3 +- 7 files changed, 101 insertions(+), 231 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 86a45af2f81..3708abf4cf6 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -8,7 +8,9 @@ import forge.card.MagicColor; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import forge.GameCommand; import forge.card.CardType; @@ -18,6 +20,7 @@ import forge.game.GameObject; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardFactoryUtil; +import forge.game.combat.Combat; import forge.game.player.Player; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementHandler; @@ -28,8 +31,11 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; +import forge.util.CardTranslation; import forge.util.Lang; +import forge.util.Localizer; import forge.util.collect.FCollection; +import forge.util.collect.FCollectionView; /** *

@@ -523,4 +529,58 @@ public abstract class SpellAbilityEffect { game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } } + + protected static boolean addToCombat(Card c, Player controller, SpellAbility sa, String attackingParam, String blockingParam) { + final Card host = sa.getHostCard(); + final Game game = controller.getGame(); + if (!game.getPhaseHandler().inCombat()) { + return false; + } + boolean combatChanged = false; + final Combat combat = game.getCombat(); + + if (sa.hasParam(attackingParam) && combat.getAttackingPlayer().equals(controller)) { + String attacking = sa.getParam(attackingParam); + GameEntity defender = null; + FCollectionView defs = null; + if ("True".equalsIgnoreCase(attacking)) { + defs = combat.getDefenders(); + } else if (sa.hasParam("ChoosePlayerOrPlaneswalker")) { + Player defendingPlayer = Iterables.getFirst(AbilityUtils.getDefinedPlayers(host, attacking, sa), null); + if (defendingPlayer != null) { + defs = game.getCombat().getDefendersControlledBy(defendingPlayer); + } + } else { + defs = AbilityUtils.getDefinedEntities(host, attacking, sa); + } + + if (defs != null) { + Map params = Maps.newHashMap(); + params.put("Attacker", c); + defender = controller.getController().chooseSingleEntityForEffect(defs, sa, + Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false, params); + } + + if (defender != null) { + combat.addAttacker(c, defender); + combatChanged = true; + } + } + if (sa.hasParam(blockingParam)) { + final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, sa.getParam(blockingParam), sa), null); + if (attacker != null) { + final boolean wasBlocked = combat.isBlocked(attacker); + combat.addBlocker(attacker, c); + combat.orderAttackersForDamageAssignment(c); + + // Run triggers for new blocker and add it to damage assignment order + if (!wasBlocked) { + combat.setBlocked(attacker, true); + combat.addBlockerToDamageAssignmentOrder(attacker, c); + } + combatChanged = true; + } + } + return combatChanged; + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 00ba1b74398..5376948270d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -11,12 +11,10 @@ import forge.card.CardStateName; import forge.game.Game; import forge.game.GameEntity; import forge.game.GameLogEntryType; -import forge.game.GameObject; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.*; -import forge.game.combat.Combat; import forge.game.event.GameEventCombatChanged; import forge.game.player.DelayedReveal; import forge.game.player.Player; @@ -483,6 +481,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { final boolean optional = sa.hasParam("Optional"); final long ts = game.getNextTimestamp(); + boolean combatChanged = false; for (final Card tgtC : tgtCards) { if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) { @@ -616,42 +615,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (sa.hasParam("FaceDown")) { movedCard.turnFaceDown(true); } - if (sa.hasParam("Attacking")) { - final Combat combat = game.getCombat(); - if (null != combat) { - FCollectionView defs = null; - String attacking = sa.getParam("Attacking"); - if ("True".equalsIgnoreCase(attacking)) { - defs = combat.getDefenders(); - } else if (sa.hasParam("ChoosePlayerOrPlaneswalker")) { - Player defendingPlayer = Iterables.getFirst(AbilityUtils.getDefinedPlayers(hostCard, - attacking, sa), null); - if (defendingPlayer != null) { - defs = combat.getDefendersControlledBy(defendingPlayer); - } - } - GameEntity defender = null; - if (sa.hasParam("DefinedDefender")) { - FCollection objs = AbilityUtils.getDefinedObjects(hostCard, sa.getParam("DefinedDefender"), sa); - for(GameObject obj : objs) { - if (obj instanceof GameEntity) { - defender = (GameEntity)obj; - break; - } - } - } else { - String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName())); - Map params = Maps.newHashMap(); - params.put("Attacker", movedCard); - defender = player.getController().chooseSingleEntityForEffect(defs, sa, title,false, - params); - } - if (defender != null) { - combat.addAttacker(movedCard, defender); - game.getCombat().getBandOfAttacker(movedCard).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); - } - } + if (addToCombat(movedCard, movedCard.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } if (sa.hasParam("Ninjutsu")) { // Ninjutsu need to get the Defender of the Returned Creature @@ -659,7 +624,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { final GameEntity defender = game.getCombat().getDefenderByAttacker(returned); game.getCombat().addAttacker(tgtC, defender); game.getCombat().getBandOfAttacker(tgtC).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); + combatChanged = true; } if (sa.hasParam("Tapped") || sa.hasParam("Ninjutsu")) { tgtC.setTapped(true); @@ -727,6 +692,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } + //reveal command cards that changes zone from command zone to player's hand if (!commandCards.isEmpty()) { game.getAction().reveal(commandCards, player, true, "Revealed cards in "); @@ -1066,6 +1036,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { CardCollection movedCards = new CardCollection(); long ts = game.getNextTimestamp(); final CardZoneTable triggerList = new CardZoneTable(); + boolean combatChanged = false; for (final Card c : chosenCards) { Card movedCard = null; final Zone originZone = game.getZoneOf(c); @@ -1148,59 +1119,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } - if (sa.hasParam("Attacking")) { - final Combat combat = game.getCombat(); - if (null != combat) { - FCollectionView defs = null; - String attacking = sa.getParam("Attacking"); - if ("True".equalsIgnoreCase(attacking)) { - defs = combat.getDefenders(); - } else if (sa.hasParam("ChoosePlayerOrPlaneswalker")) { - Player defendingPlayer = Iterables.getFirst(AbilityUtils.getDefinedPlayers(source, - attacking, sa), null); - if (defendingPlayer != null) { - defs = combat.getDefendersControlledBy(defendingPlayer); - } - } - GameEntity defender = null; - if (sa.hasParam("DefinedDefender")) { - FCollection objs = AbilityUtils.getDefinedObjects(source, - sa.getParam("DefinedDefender"), sa); - for (GameObject obj : objs) { - if (obj instanceof GameEntity) { - defender = (GameEntity) obj; - break; - } - } - } else { - String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", - CardTranslation.getTranslatedName(c.getName())); - Map params = Maps.newHashMap(); - params.put("Attacker", c); - defender = decider.getController().chooseSingleEntityForEffect(defs, sa, title,false, - params); - } - if (defender != null) { - combat.addAttacker(c, defender); - game.getCombat().getBandOfAttacker(c).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); - } - } - } - if (sa.hasParam("Blocking")) { - final Combat combat = game.getCombat(); - if ( null != combat ) { - CardCollection attackers = AbilityUtils.getDefinedCards(source, sa.getParam("Blocking"), sa); - if (!attackers.isEmpty()) { - Card attacker = attackers.get(0); - if (combat.isAttacking(attacker)) { - combat.addBlocker(attacker, c); - combat.orderAttackersForDamageAssignment(c); - game.fireEvent(new GameEventCombatChanged()); - } - } - } + if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } + // need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger if (sa.hasParam("FaceDown") && ZoneType.Battlefield.equals(destination)) { c.turnFaceDown(true); @@ -1302,6 +1224,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { player.shuffle(sa); } + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } triggerList.triggerChangesZoneAll(game); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java index e0e2f7ca921..2555f733221 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java @@ -2,28 +2,21 @@ package forge.game.ability.effects; import java.util.Arrays; import java.util.List; -import java.util.Map; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import forge.GameCommand; import forge.game.Game; -import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.combat.Combat; import forge.game.event.GameEventCardStatsChanged; import forge.game.event.GameEventCombatChanged; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.collect.FCollectionView; -import forge.util.Localizer; -import forge.util.CardTranslation; public class ControlGainEffect extends SpellAbilityEffect { @@ -86,7 +79,6 @@ public class ControlGainEffect extends SpellAbilityEffect { final boolean bTapOnLose = sa.hasParam("TapOnLose"); final boolean remember = sa.hasParam("RememberControlled"); final boolean forget = sa.hasParam("ForgetControlled"); - final boolean attacking = sa.hasParam("Attacking"); final List keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null; final List lose = sa.hasParam("LoseControl") ? Arrays.asList(sa.getParam("LoseControl").split(",")) : null; @@ -109,6 +101,7 @@ public class ControlGainEffect extends SpellAbilityEffect { return; } + boolean combatChanged = false; for (Card tgtC : tgtCards) { if (!tgtC.isInPlay() || !tgtC.canBeControlledBy(newController)) { @@ -209,23 +202,15 @@ public class ControlGainEffect extends SpellAbilityEffect { game.getAction().controllerChangeZoneCorrection(tgtC); - if (attacking) { - final Combat combat = game.getCombat(); - if ( null != combat ) { - final FCollectionView e = combat.getDefenders(); - String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName())); - Map params = Maps.newHashMap(); - params.put("Attacker", tgtC); - final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa, title, params); - - if (defender != null) { - combat.addAttacker(tgtC, defender); - game.getCombat().getBandOfAttacker(tgtC).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); - } - } + if (addToCombat(tgtC, tgtC.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } } // end foreach target + + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } } /** diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index d3052689056..24a9c9fd7d8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -1,11 +1,8 @@ package forge.game.ability.effects; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; import forge.card.MagicColor; import forge.game.Game; import forge.game.GameActionUtil; -import forge.game.GameEntity; import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -16,7 +13,6 @@ import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.card.CardZoneTable; import forge.game.card.CounterType; -import forge.game.combat.Combat; import forge.game.event.GameEventCombatChanged; import forge.game.player.DelayedReveal; import forge.game.player.Player; @@ -109,6 +105,7 @@ public class DigEffect extends SpellAbilityEffect { CardZoneTable table = new CardZoneTable(); GameEntityCounterTable counterTable = new GameEntityCounterTable(); + boolean combatChanged = false; for (final Player p : tgtPlayers) { if (tgt != null && !p.canBeTargetedBy(sa)) { continue; @@ -317,35 +314,8 @@ public class DigEffect extends SpellAbilityEffect { if (sa.hasParam("Tapped")) { c.setTapped(true); } - if (sa.hasParam("Attacking")) { - final Combat combat = game.getCombat(); - String attacking = sa.getParam("Attacking"); - GameEntity defender = null; - FCollectionView defs = null; - if (null != combat) { - if ("True".equalsIgnoreCase(attacking)) { - defs = combat.getDefenders(); - } else if (sa.hasParam("ChoosePlayerOrPlaneswalker")) { - Player defendingPlayer = Iterables.getFirst(AbilityUtils.getDefinedPlayers(host, - attacking, sa), null); - if (defendingPlayer != null) { - defs = game.getCombat().getDefendersControlledBy(defendingPlayer); - } - } - } - if (defs != null) { - Map params = Maps.newHashMap(); - params.put("Attacker", c); - defender = player.getController().chooseSingleEntityForEffect(defs, sa, - Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", - CardTranslation.getTranslatedName(c.getName())),false, - params); - } - if (defender != null) { - combat.addAttacker(c, defender); - game.getCombat().getBandOfAttacker(c).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); - } + if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } } else if (destZone1.equals(ZoneType.Exile)) { c.setExiledWith(effectHost); @@ -423,6 +393,10 @@ public class DigEffect extends SpellAbilityEffect { } } } + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } //table trigger there table.triggerChangesZoneAll(game); counterTable.triggerCountersPutAll(game); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index eff16192f72..405e61faf3f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -1,27 +1,21 @@ package forge.game.ability.effects; import forge.game.Game; -import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardZoneTable; -import forge.game.combat.Combat; import forge.game.event.GameEventCombatChanged; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; -import forge.util.CardTranslation; import forge.util.MyRandom; import forge.util.Localizer; -import forge.util.collect.FCollectionView; import java.util.*; -import com.google.common.collect.Maps; - public class DigUntilEffect extends SpellAbilityEffect { /* (non-Javadoc) @@ -116,6 +110,7 @@ public class DigUntilEffect extends SpellAbilityEffect { final boolean optionalFound = sa.hasParam("OptionalFoundMove"); CardZoneTable table = new CardZoneTable(); + boolean combatChanged = false; for (final Player p : getTargetPlayers(sa)) { if (p == null) { @@ -178,23 +173,8 @@ public class DigUntilEffect extends SpellAbilityEffect { if (sa.hasParam("Tapped")) { c.setTapped(true); } - if (sa.hasParam("Attacking")) { - final Combat combat = game.getCombat(); - if (null != combat) { - final FCollectionView e = combat.getDefenders(); - - Map params = Maps.newHashMap(); - params.put("Attacker", c); - - final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa, - Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), params); - - if (defender != null) { - combat.addAttacker(c, defender); - combat.getBandOfAttacker(c).setBlocked(false); - game.fireEvent(new GameEventCombatChanged()); - } - } + if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) { + combatChanged = true; } } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) { //Don't do anything @@ -268,6 +248,10 @@ public class DigUntilEffect extends SpellAbilityEffect { } } // end foreach player } + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } table.triggerChangesZoneAll(game); } // end resolve diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java index 52ec2a03853..6ff536eac70 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java @@ -2,13 +2,11 @@ package forge.game.ability.effects; import java.util.Arrays; import java.util.List; -import java.util.Map; import org.apache.commons.lang3.mutable.MutableBoolean; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import forge.GameCommand; @@ -22,14 +20,10 @@ import forge.game.card.CardCollection; import forge.game.card.CardUtil; import forge.game.card.CardZoneTable; import forge.game.card.token.TokenInfo; -import forge.game.combat.Combat; import forge.game.event.GameEventCardStatsChanged; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.CardTranslation; -import forge.util.Localizer; -import forge.util.collect.FCollectionView; public abstract class TokenEffectBase extends SpellAbilityEffect { @@ -80,7 +74,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect { addSelfTrigger(sa, sa.getParam("AtEOTTrig"), c); } - if (addTokenToCombat(game, c, tok.getController(), sa, host)) { + if (addToCombat(c, tok.getController(), sa, "TokenAttacking", "TokenBlocking")) { combatChanged.setTrue(); } @@ -115,58 +109,6 @@ public abstract class TokenEffectBase extends SpellAbilityEffect { return allTokens; } - private boolean addTokenToCombat(Game game, Card c, Player controller, SpellAbility sa, Card host) { - if (!game.getPhaseHandler().inCombat()) { - return false; - } - boolean combatChanged = false; - final Combat combat = game.getCombat(); - - if (sa.hasParam("TokenAttacking") && combat.getAttackingPlayer().equals(controller)) { - String attacking = sa.getParam("TokenAttacking"); - GameEntity defender = null; - FCollectionView defs = null; - if ("True".equalsIgnoreCase(attacking)) { - defs = combat.getDefenders(); - } else if (sa.hasParam("ChoosePlayerOrPlaneswalker")) { - Player defendingPlayer = Iterables.getFirst(AbilityUtils.getDefinedPlayers(host, attacking, sa), null); - if (defendingPlayer != null) { - defs = game.getCombat().getDefendersControlledBy(defendingPlayer); - } - } else { - defs = AbilityUtils.getDefinedEntities(host, attacking, sa); - } - - if (defs != null) { - Map params = Maps.newHashMap(); - params.put("Attacker", c); - defender = controller.getController().chooseSingleEntityForEffect(defs, sa, - Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false, params); - } - - if (defender != null) { - combat.addAttacker(c, defender); - combatChanged = true; - } - } - if (sa.hasParam("TokenBlocking")) { - final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, sa.getParam("TokenBlocking"), sa), null); - if (attacker != null) { - final boolean wasBlocked = combat.isBlocked(attacker); - combat.addBlocker(attacker, c); - combat.orderAttackersForDamageAssignment(c); - - // Run triggers for new blocker and add it to damage assignment order - if (!wasBlocked) { - combat.setBlocked(attacker, true); - combat.addBlockerToDamageAssignmentOrder(attacker, c); - } - combatChanged = true; - } - } - return combatChanged; - } - private boolean attachTokenTo(Card tok, SpellAbility sa) { final Card host = sa.getHostCard(); final Game game = host.getGame(); diff --git a/forge-gui/res/cardsfolder/k/kaalia_of_the_vast.txt b/forge-gui/res/cardsfolder/k/kaalia_of_the_vast.txt index a23ecf9480c..f48da2c51da 100644 --- a/forge-gui/res/cardsfolder/k/kaalia_of_the_vast.txt +++ b/forge-gui/res/cardsfolder/k/kaalia_of_the_vast.txt @@ -4,6 +4,5 @@ Types:Legendary Creature Human Cleric PT:2/2 K:Flying T:Mode$ Attacks | ValidCard$ Card.Self | Attacked$ Opponent | Execute$ TrigChange | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks an opponent, you may put an Angel, Demon, or Dragon creature card from your hand onto the battlefield tapped and attacking that opponent. -SVar:TrigChange:DB$ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.Angel+YouCtrl,Creature.Demon+YouCtrl,Creature.Dragon+YouCtrl | Tapped$ True | Attacking$ True | DefinedDefender$ TriggeredDefender -SVar:Picture:http://www.wizards.com/global/images/magic/general/kaalia_of_the_vast.jpg +SVar:TrigChange:DB$ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.Angel+YouCtrl,Creature.Demon+YouCtrl,Creature.Dragon+YouCtrl | Tapped$ True | Attacking$ TriggeredDefender Oracle:Flying\nWhenever Kaalia of the Vast attacks an opponent, you may put an Angel, Demon, or Dragon creature card from your hand onto the battlefield tapped and attacking that opponent.