diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index be3959b2ac7..12d19649791 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -428,6 +428,7 @@ public class GameAction { if (!c.isToken() && !toBattlefield) { copied.clearDevoured(); copied.clearDelved(); + copied.clearConvoked(); } // rule 504.6: reveal a face-down card leaving the stack diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java index 116b5aae57b..6d7b25e97d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java @@ -8,6 +8,7 @@ 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.CardFactory; import forge.game.card.CardLists; import forge.game.player.Player; @@ -123,7 +124,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { } } else {// Precursor Golem, Ink-Treader Nephilim final String type = sa.getParam("CopyForEachCanTarget"); - List valid = Lists.newArrayList(); + CardCollection valid = new CardCollection(); List players = Lists.newArrayList(); Player originalTargetPlayer = Iterables.getFirst(getTargetPlayers(chosenSA), null); for (final GameEntity o : candidates) { @@ -142,10 +143,17 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { Card originalTarget = Iterables.getFirst(getTargetCards(chosenSA), null); valid.remove(originalTarget); mayChooseNewTargets = false; - for (final Card c : valid) { + if (sa.hasParam("ChooseOnlyOne")) { + Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, "Choose one"); SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); - resetFirstTargetOnCopy(copy, c, targetedSA); + resetFirstTargetOnCopy(copy, choice, targetedSA); copies.add(copy); + } else { + for (final Card c : valid) { + SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); + resetFirstTargetOnCopy(copy, c, targetedSA); + copies.add(copy); + } } for (final Player p : players) { SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f56315a4a29..1a9ed880a56 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -98,7 +98,7 @@ public class Card extends GameEntity implements Comparable { private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection(); // cards attached or otherwise linked to this card - private CardCollection equippedBy, fortifiedBy, hauntedBy, devouredCards, delvedCards, imprintedCards, encodedCards; + private CardCollection equippedBy, fortifiedBy, hauntedBy, devouredCards, delvedCards, convokedCards, imprintedCards, encodedCards; private CardCollection mustBlockCards, clones, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn; // if this card is attached or linked to something, what card is it currently attached to @@ -725,6 +725,20 @@ public class Card extends GameEntity implements Comparable { delvedCards = null; } + + public final CardCollectionView getConvoked() { + return CardCollection.getView(convokedCards); + } + public final void addConvoked(final Card c) { + if (convokedCards == null) { + convokedCards = new CardCollection(); + } + convokedCards.add(c); + } + public final void clearConvoked() { + convokedCards = null; + } + public MapOfLists getRememberMap() { return rememberMap; } diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 4a480acb239..d2be541192c 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1378,6 +1378,10 @@ public class CardProperty { if (!source.getDelved().contains(card)) { return false; } + } else if (property.startsWith("convoked")) { + if (!source.getConvoked().contains(card)) { + return false; + } } else if (property.startsWith("unequalPT")) { if (card.getNetPower() == card.getNetToughness()) { return false; diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index 70d6c91083d..455949c58d5 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -237,6 +237,7 @@ public class CostAdjustment { } } if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) { + sa.getHostCard().clearConvoked(); adjustCostByConvokeOrImprovise(cost, sa, false, test); } if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) { @@ -270,6 +271,9 @@ public class CostAdjustment { cost.decreaseShard(conv.getValue(), 1); if (!test) { conv.getKey().tap(); + if (!improvise) { + sa.getHostCard().addConvoked(conv.getKey()); + } } } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java index 74646a8b98b..cd6ceb7ceab 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java @@ -24,8 +24,10 @@ import java.util.Set; import com.google.common.collect.Sets; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GameObject; import forge.game.card.Card; +import forge.game.card.CardCollection; import forge.game.card.CardLists; import forge.game.card.CardUtil; import forge.game.cost.Cost; @@ -158,6 +160,31 @@ public class TriggerSpellAbilityCast extends Trigger { } } + if (hasParam("CanTargetOtherCondition")) { + final CardCollection candidates = new CardCollection(); + SpellAbility targetedSA = spellAbility; + while (targetedSA != null) { + if (targetedSA.usesTargeting() && targetedSA.getTargets().getNumTargeted() != 0) { + break; + } + targetedSA = targetedSA.getSubAbility(); + } + if (targetedSA == null) { + return false; + } + final List candidateTargets = targetedSA.getTargetRestrictions().getAllCandidates(targetedSA, true); + for (GameEntity card : candidateTargets) { + if (card instanceof Card) { + candidates.add((Card) card); + } + } + candidates.removeAll(targetedSA.getTargets().getTargetCards()); + String valid = this.mapParams.get("CanTargetOtherCondition"); + if (CardLists.getValidCards(candidates, valid, spellAbility.getActivatingPlayer(), spellAbility.getHostCard()).isEmpty()) { + return false; + } + } + if (hasParam("NonTapCost")) { final Cost cost = (Cost) (runParams2.get("Cost")); if (cost.hasTapCost()) { diff --git a/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt b/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt new file mode 100644 index 00000000000..66f2bc8fe77 --- /dev/null +++ b/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt @@ -0,0 +1,7 @@ +Name:Beamsplitter Mage +ManaCost:U R +Types:Creature Vedalken Wizard +PT:2/2 +T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | IsSingleTarget$ True | TargetsValid$ Card.Self | CanTargetOtherCondition$ Creature.YouCtrl | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an instant or sorcery that targets only CARDNAME, if you control one or more other creatures that spell could target, choose one of those creatures. Copy that spell. The copy targets the chosen creature. +SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Controller$ You | CopyForEachCanTarget$ Creature.YouCtrl | ChooseOnlyOne$ True +Oracle:Whenever you cast an instant or sorcery spell that targets only Beamsplitter Mage, if you control one or more other creatures that spell could target, choose one of those creatures. Copy that spell. The copy targets the chosen creature. diff --git a/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt b/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt new file mode 100644 index 00000000000..e8f35ff5bfa --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt @@ -0,0 +1,10 @@ +Name:Lazav, the Multifarious +ManaCost:U B +Types:Legendary Creature Shapeshifter +PT:1/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSurveil | TriggerDescription$ When CARDNAME enters the battlefield, scry 1. (To scry 1, look at the top card of your library. You may put that card into your graveyard.) +SVar:TrigSurveil:DB$ Surveil | Amount$ 1 +A:AB$ Clone | Cost$ X | ValidTgts$ Creature.YouOwn | References$ X | TgtZone$ Graveyard | TgtPrompt$ Select target creature card in your graveyard | AddTypes$ Legendary | KeepName$ True | AddSVars$ X,LazavTrig | AddAbilities$ LazavTrig | SpellDescription$ CARDNAME becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is CARDNAME, it's legendary in addition to it's other types, and it has this ability. +SVar:LazavTrig:AB$ Clone | Cost$ X | ValidTgts$ Creature.YouOwn | References$ X | TgtZone$ Graveyard | TgtPrompt$ Select target creature card in your graveyard | AddTypes$ Legendary | KeepName$ True | AddSVars$ X,LazavTrig | AddAbilities$ LazavTrig | SpellDescription$ CARDNAME becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is CARDNAME, it's legendary in addition to it's other types, and it has this ability. +SVar:X:Targeted$CardManaCost +Oracle:When Lazav, the Multifarious enters the battlefield, surveil 1. (Look at the top card of your library. You may put that card into your graveyard.)\n{X}: Lazav, the Multifarious becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is Lazav, the Multifarious, it's legendary in addition to its other types, and it has this ability. diff --git a/forge-gui/res/cardsfolder/v/venerated_loxodon.txt b/forge-gui/res/cardsfolder/v/venerated_loxodon.txt new file mode 100644 index 00000000000..f5add8bf2ce --- /dev/null +++ b/forge-gui/res/cardsfolder/v/venerated_loxodon.txt @@ -0,0 +1,8 @@ +Name:Venerated Loxodon +ManaCost:4 W +Types:Creature Elephant Cleric +PT:4/4 +K:Convoke +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounterAll | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on each creature that convoked it. +SVar:TrigPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.convoked | CounterType$ P1P1 | CounterNum$ 1 +Oracle:Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of the creature's color.)\nWhen Venerated Loxodon enters the battlefield, put a +1/+1 counter on each creature that convoked it.